File Coverage

lib/Web/DataService.pm
Criterion Covered Total %
statement 203 502 40.4
branch 48 308 15.5
condition 31 220 14.0
subroutine 44 85 51.7
pod 11 26 42.3
total 337 1141 29.5


line stmt bran cond sub pod time code
1             #
2             # DataService.pm
3             #
4             # This is a framework for building data service applications.
5             #
6             # Author: Michael McClennen
7              
8              
9 2     2   636638 use strict;
  2         17  
  2         140  
10              
11             require 5.012;
12              
13             =head1 NAME
14              
15             Web::DataService - a framework for building data service applications for the Web
16              
17             =head1 VERSION
18              
19             Version 0.41
20              
21             =head1 SYNOPSIS
22              
23             This module provides a framework for you to use in building data service
24             applications for the World Wide Web. Such applications sit between a data
25             storage and retrieval system on one hand and the Web on the other, and fulfill
26             HTTP-based data requests. Each valid request is handled by fetching or
27             storing the appropriate data using the backend data system and serializing the
28             output in a format such as JSON, CSV, or XML.
29              
30             Using the methods provided by this module, you start by defining a set of data
31             service elements: output formats, output blocks, vocabularies, and parameter
32             rules, followed by a set of data service nodes representing the various
33             operations to be provided by your service. Each of these objects is
34             configured by a set of attributes, optionally including documentation strings.
35             You continue by writing one or more modules whose methods will carry out the
36             core part of each data service operation: talking to the backend data system
37             to fetch and/or store the relevant data, based on the parameter values
38             provided in a data service request.
39              
40             The Web::DataService code then takes care of most of the work necessary for
41             handling each request, including checking the parameter values, determining
42             the response format, calling your operation method at the appropriate time,
43             and serializing the result. It also generates appropriate error messages when
44             necessary. Finally, it auto-generates documentation pages for each operation
45             based on the elements you have defined, so that your data service is always
46             fully and correctly documented.
47              
48             A Web::DataService application is built on top of a "foundation framework"
49             that provides the basic functionality of parsing HTTP requests and
50             constructing responses. At the present time, the only one that can be used is
51             L. However, we plan to add compatibility with other frameworks such
52             as Mojolicious and Catalyst soon.
53              
54             =cut
55              
56             package Web::DataService;
57              
58             our $VERSION = '0.41';
59              
60 2     2   15 use feature qw(say);
  2         4  
  2         233  
61              
62 2     2   19 use Carp qw( carp croak confess );
  2         4  
  2         143  
63 2     2   36 use Scalar::Util qw( reftype blessed weaken );
  2         5  
  2         122  
64 2     2   1175 use POSIX qw( strftime );
  2         13501  
  2         14  
65 2     2   4681 use HTTP::Validate;
  2         20479  
  2         129  
66              
67 2     2   1175 use Web::DataService::Node;
  2         11  
  2         87  
68 2     2   957 use Web::DataService::Set;
  2         5  
  2         62  
69 2     2   815 use Web::DataService::Format;
  2         7  
  2         62  
70 2     2   873 use Web::DataService::Vocabulary;
  2         5  
  2         64  
71 2     2   936 use Web::DataService::Ruleset;
  2         7  
  2         76  
72 2     2   894 use Web::DataService::Render;
  2         6  
  2         62  
73 2     2   1326 use Web::DataService::Output;
  2         9  
  2         112  
74 2     2   1284 use Web::DataService::Execute;
  2         6  
  2         98  
75 2     2   1019 use Web::DataService::Document;
  2         10  
  2         71  
76 2     2   1041 use Web::DataService::Diagnostic;
  2         5  
  2         76  
77              
78 2     2   1218 use Web::DataService::Request;
  2         9  
  2         94  
79 2     2   1222 use Web::DataService::IRequest;
  2         7  
  2         77  
80 2     2   940 use Web::DataService::IDocument;
  2         7  
  2         61  
81 2     2   1225 use Web::DataService::PodParser;
  2         8  
  2         91  
82              
83 2     2   20 use Moo;
  2         8  
  2         27  
84 2     2   1000 use namespace::clean;
  2         5  
  2         24  
85              
86             with 'Web::DataService::Node', 'Web::DataService::Set',
87             'Web::DataService::Format', 'Web::DataService::Vocabulary',
88             'Web::DataService::Ruleset', 'Web::DataService::Render',
89             'Web::DataService::Output', 'Web::DataService::Execute',
90             'Web::DataService::Document', 'Web::DataService::Diagnostic';
91              
92              
93             our (@CARP_NOT) = qw(Web::DataService::Request Web::DataService::Node Moo);
94              
95             HTTP::Validate->VERSION(0.47);
96              
97              
98             our @HTTP_METHOD_LIST = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE');
99              
100             our @DEFAULT_METHODS = ('GET', 'HEAD');
101              
102             our %SPECIAL_FEATURE = (format_suffix => 1, documentation => 1,
103             doc_paths => 1, send_files => 1, strict_params => 1,
104             stream_output => 1);
105              
106             our @FEATURE_STANDARD = ('format_suffix', 'documentation', 'doc_paths',
107             'send_files', 'strict_params', 'stream_output');
108              
109             our @FEATURE_ALL = ('format_suffix', 'documentation', 'doc_paths',
110             'send_files', 'strict_params', 'stream_output');
111              
112             our %SPECIAL_PARAM = (selector => 'v', format => 'format', path => 'op',
113             document => 'document', show => 'show',
114             limit => 'limit', offset => 'offset',
115             count => 'count', vocab => 'vocab',
116             datainfo => 'datainfo', linebreak => 'lb',
117             header => 'header', save => 'save');
118              
119             our @SPECIAL_STANDARD = ('show', 'limit', 'offset', 'header', 'datainfo',
120             'count', 'vocab', 'linebreak', 'save');
121              
122             our @SPECIAL_SINGLE = ('selector', 'path', 'format', 'show', 'header',
123             'datainfo', 'vocab', 'linebreak', 'save');
124              
125             our @SPECIAL_ALL = ('selector', 'path', 'document', 'format', 'show',
126             'limit', 'offset', 'header', 'datainfo', 'count',
127             'vocab', 'linebreak', 'save');
128              
129             my (@DI_KEYS) = qw(data_provider data_source data_license license_url
130             documentation_url data_url access_time title);
131              
132              
133             # Execution modes
134              
135             our ($DEBUG, $ONE_REQUEST, $ONE_PROCESS, $CHECK_LATER, $QUIET, $DIAGNOSTIC);
136              
137              
138             # Variables for keeping track of data service instances
139              
140             my (%KEY_MAP, %PREFIX_MAP);
141             our (@WDS_INSTANCES);
142             our ($FOUNDATION);
143              
144              
145             # Attributes of a Web::DataService object
146              
147             has name => ( is => 'ro', required => 1,
148             isa => \&_valid_name );
149              
150             has parent => ( is => 'ro', init_arg => '_parent' );
151              
152             has features => ( is => 'ro', required => 1 );
153              
154             has special_params => ( is => 'ro', required => 1 );
155              
156 1     1   14 has templating_plugin => ( is => 'lazy', builder => sub { $_[0]->_init_value('templating_plugin') } );
157              
158 1     1   12 has backend_plugin => ( is => 'lazy', builder => sub { $_[0]->_init_value('backend_plugin') } );
159              
160 0     0   0 has title => ( is => 'lazy', builder => sub { $_[0]->_init_value('title') } );
161              
162 0     0   0 has version => ( is => 'lazy', builder => sub { $_[0]->_init_value('version') } );
163              
164 0     0   0 has path_prefix => ( is => 'lazy', builder => sub { $_[0]->_init_value('path_prefix') } );
165              
166 1     1   11 has path_re => ( is => 'lazy', builder => sub { $_[0]->_init_value('path_re') } );
167              
168 1     1   12 has key => ( is => 'lazy', builder => sub { $_[0]->_init_value('key') } );
169              
170 0     0   0 has hostname => ( is => 'lazy', builder => sub { $_[0]->_init_value('hostname') } );
171              
172 0     0   0 has port => ( is => 'lazy', builder => sub { $_[0]->_init_value('port') } );
173              
174             has generate_url_hook => ( is => 'rw', isa => \&_code_ref );
175              
176 0     0   0 has ruleset_prefix => ( is => 'lazy', builder => sub { $_[0]->_init_value('ruleset_prefix') } );
177              
178 0     0   0 has doc_suffix => ( is => 'lazy', builder => sub { $_[0]->_init_value('doc_suffix') } );
179              
180 0     0   0 has doc_index => ( is => 'lazy', builder => sub { $_[0]->_init_value('doc_index') } );
181              
182 0     0   0 has doc_template_dir => ( is => 'lazy', builder => sub { $_[0]->_init_value('doc_template_dir') } );
183              
184 0     0   0 has doc_compile_dir => ( is => 'lazy', builder => sub { $_[0]->_init_value('doc_compiled_dir') } );
185              
186 0     0   0 has output_template_dir => ( is => 'lazy', builder => sub { $_[0]->_init_value('output_template_dir') } );
187              
188 0     0   0 has output_compile_dir => ( is => 'lazy', builder => sub { $_[0]->_init_value('output_compiled_dir') } );
189              
190 0     0   0 has data_source => ( is => 'lazy', builder => sub { $_[0]->_init_value('data_source') } );
191              
192 0     0   0 has data_provider => ( is => 'lazy', builder => sub { $_[0]->_init_value('data_provider') } );
193              
194 0     0   0 has data_license => ( is => 'lazy', builder => sub { $_[0]->_init_value('data_license') } );
195              
196 0     0   0 has license_url => ( is => 'lazy', builder => sub { $_[0]->_init_value('license_url') } );
197              
198 0     0   0 has contact_name => ( is => 'lazy', builder => sub { $_[0]->_init_value('contact_name') } );
199              
200 0     0   0 has contact_email => ( is => 'lazy', builder => sub { $_[0]->_init_value('contact_email') } );
201              
202             has validator => ( is => 'ro', init_arg => undef );
203              
204              
205             # Validator methods for the data service attributes.
206              
207             sub _valid_name {
208              
209 1 50   1   4853 die "not a valid name"
210             unless $_[0] =~ qr{ ^ [\w.:][\w.:-]* $ }xs;
211             }
212              
213              
214             sub _code_ref {
215              
216 0 0 0 0   0 die "must be a code ref"
217             unless ref $_[0] && reftype $_[0] eq 'CODE';
218             }
219              
220              
221             # BUILD ( )
222             #
223             # This method is called automatically after object initialization.
224              
225             sub BUILD {
226              
227 1     1 0 16 my ($self) = @_;
228            
229 1         5 local($Carp::CarpLevel) = 1; # We shouldn't have to do this, but
230             # Moo and Carp don't play well together.
231            
232             # If no path prefix was defined, make it the empty string.
233            
234 1   50     9 $self->{path_prefix} //= '';
235            
236             # Process the feature list
237             # ------------------------
238            
239             # These may be specified either as a listref or as a string with
240             # comma-separated values.
241            
242 1         6 my $features_value = $self->features;
243 1 50       10 my @features = ref $features_value eq 'ARRAY' ? @$features_value : split /\s*,\s*/, $features_value;
244            
245             ARG:
246 1         3 foreach my $o ( @features )
247             {
248 1 50 33     8 next unless defined $o && $o ne '';
249            
250 1         3 my $feature_value = 1;
251 1         2 my $key = $o;
252            
253             # If 'standard' was specified, enable the standard set of features.
254             # (But don't override any that have already been set or cleared
255             # explicitly.)
256            
257 1 50       5 if ( $o eq 'standard' )
    0          
258             {
259 1         2 foreach my $p ( @FEATURE_STANDARD )
260             {
261 6   50     21 $self->{feature}{$p} //= 1;
262             }
263            
264 1         5 next ARG;
265             }
266            
267             # If we get an argument that looks like 'no_feature', then disable
268             # the feature.
269            
270             elsif ( $o =~ qr{ ^ no_ (\w+) $ }xs )
271             {
272 0         0 $key = $1;
273 0         0 $feature_value = 0;
274             }
275            
276             # Now, complain if the user gives us something unrecognized.
277            
278 0 0       0 croak "unknown feature '$o'\n" unless $SPECIAL_FEATURE{$key};
279            
280             # Give this parameter the specified value (either on or off).
281             # Parameters not mentioned default to off, unless 'standard' was
282             # included.
283            
284 0         0 $self->{feature}{$key} = $feature_value;
285             }
286            
287             # Process the list of special parameters
288             # --------------------------------------
289            
290             # These may be specified either as a listref or as a string with
291             # comma-separated values.
292            
293 1         5 my $special_value = $self->special_params;
294 1 50       7 my @specials = ref $special_value eq 'ARRAY' ? @$special_value : split /\s*,\s*/, $special_value;
295            
296             ARG:
297 1         4 foreach my $s ( @specials )
298             {
299 1 50 33     8 next unless defined $s && $s ne '';
300 1         3 my $key = $s;
301 1         4 my $name = $SPECIAL_PARAM{$s};
302 1         2 my @aliases;
303            
304             # If 'standard' was specified, enable the "standard" set of parameters
305             # with their default names (but don't override any that have already
306             # been enabled).
307            
308 1 50       5 if ( $s eq 'standard' )
    0          
    0          
309             {
310 1         3 foreach my $p ( @SPECIAL_STANDARD )
311             {
312 9   33     37 $self->{special}{$p} //= $SPECIAL_PARAM{$p};
313             }
314            
315 1         2 next ARG;
316             }
317            
318             # If we get an argument that looks like 'no_param', then disable
319             # the parameter.
320            
321             elsif ( $s =~ qr{ ^ no_ (\w+) $ }xs )
322             {
323 0         0 $key = $1;
324 0         0 $name = '';
325             }
326            
327             # If we get an argument that looks like 'param=name', then enable the
328             # feature 'param' but use 'name' as the accepted parameter name.
329            
330             elsif ( $s =~ qr{ ^ (\w+) = (\w+) (?: / ( \w [/\w]+ ) )? $ }xs )
331             {
332 0         0 $key = $1;
333 0         0 $name = $2;
334            
335 0 0       0 if ( $3 )
336             {
337 0         0 @aliases = grep { qr{ \w } } split(qr{/}, $3);
  0         0  
338             }
339             }
340            
341             # Now, complain if the user gives us something unrecognized, or an
342             # invalid parameter name.
343            
344 0 0       0 croak "unknown special parameter '$key'\n" unless $SPECIAL_PARAM{$key};
345 0 0       0 croak "invalid parameter name '$name' - bad character\n" if $name =~ qr{[^\w/]};
346            
347             # Enable this parameter with the specified name. If any aliases were
348             # specified, then record them.
349            
350 0         0 $self->{special}{$key} = $name;
351 0 0       0 $self->{special_alias}{$key} = \@aliases if @aliases;
352             }
353            
354             # Make sure there are no feature or special parameter conflicts.
355            
356             croak "you may not specify the feature 'format_suffix' together with the special parameter 'format'"
357 1 50 33     8 if $self->{feature}{format_suffix} && $self->{special}{format};
358            
359             croak "you may not specify the feature 'doc_paths' together with the special parameter 'document'"
360 1 50 33     7 if $self->{feature}{doc_paths} && $self->{special}{document};
361            
362 1 50       4 $self->{feature}{doc_paths} = 0 unless $self->{feature}{documentation};
363            
364             # Check and configure the foundation plugin
365             # -----------------------------------------
366            
367 1         7 $self->set_foundation;
368            
369             # From this point on, we will be able to read the configuration file
370             # (assuming that a valid one is present). So do so.
371            
372 1         4 $FOUNDATION->read_config($self);
373            
374             # Check and configure the templating plugin
375             # -----------------------------------------
376            
377             # Note that unlike the foundation plugin, different data service instances
378             # may use different templating plugins.
379            
380             # If a templating plugin was explicitly specified, either in the code
381             # or in the configuration file, check that it is valid.
382            
383 1 50 33     27 if ( my $templating_plugin = $self->templating_plugin )
    50          
384             {
385 0 0       0 eval "require $templating_plugin" or croak $@;
386            
387 0 0       0 croak "$templating_plugin is not a valid templating plugin: cannot find method 'render_template'\n"
388             unless $templating_plugin->can('render_template');
389             }
390            
391             # Otherwise, if 'Template.pm' has already been required then install the
392             # corresponding plugin.
393            
394             elsif ( $INC{'Template.pm'} && ! defined $self->templating_plugin )
395             {
396 0 0       0 require Web::DataService::Plugin::TemplateToolkit or croak $@;
397 0         0 $self->{templating_plugin} = 'Web::DataService::Plugin::TemplateToolkit';
398             }
399            
400             # Otherwise, templating will not be available.
401            
402             else
403             {
404 1 50       5 if ( $self->{feature}{documentation} )
405             {
406 1 0 33     4 unless ( $QUIET || $ENV{WDS_QUIET} )
407             {
408 0         0 warn "WARNING: no templating engine was specified, so documentation pages\n";
409 0         0 warn " and templated output will not be available.\n";
410             }
411 1         2 $self->{feature}{documentation} = 0;
412 1         2 $self->{feature}{doc_paths} = 0;
413             }
414            
415 1         3 $self->{templating_plugin} = 'Web::DataService::Plugin::Templating';
416             }
417            
418             # If we have a templating plugin, instantiate it for documentation and
419             # output.
420            
421 1 50 33     8 if ( defined $self->{templating_plugin} &&
422             $self->{templating_plugin} ne 'Web::DataService::Plugin::Templating' )
423             {
424             # Let the plugin do whatever initialization it needs to.
425            
426 0         0 $self->_init_plugin('templating_plugin');
427            
428             # If no document template directory was specified, use 'doc' if it
429             # exists and is readable.
430            
431 0         0 my $doc_dir = $self->doc_template_dir;
432 0         0 my $doc_comp = $self->doc_compile_dir;
433 0         0 my $output_dir = $self->output_template_dir;
434 0         0 my $output_comp = $self->output_compile_dir;
435            
436 0 0       0 unless ( defined $doc_dir )
437             {
438 0         0 my $default = 'doc';
439            
440 0 0       0 if ( -r $default )
    0          
441             {
442 0         0 $doc_dir = $default;
443             }
444            
445             elsif ( $self->{feature}{documentation} )
446             {
447 0 0 0     0 unless ( $QUIET || $ENV{WDS_QUIET} )
448             {
449 0         0 warn "WARNING: no document template directory was found, so documentation pages\n";
450 0         0 warn " will not be available. Try putting them in the directory 'doc',\n";
451 0         0 warn " or specifying the attribute 'doc_template_dir'.\n";
452             }
453 0         0 $self->{feature}{documentation} = 0;
454 0         0 $self->{feature}{doc_paths} = 0;
455             }
456             }
457            
458             # If we were given a directory for documentation templates, initialize
459             # an engine for evaluating them.
460            
461 0 0       0 if ( $doc_dir )
462             {
463 0 0       0 $doc_dir = './' . $doc_dir
464             unless $doc_dir =~ qr{ ^ / }xs;
465            
466 0 0       0 croak "the documentation template directory '$doc_dir' is not readable: $!\n"
467             unless -r $doc_dir;
468            
469 0         0 $self->{doc_template_dir} = $doc_dir;
470            
471             $self->{doc_engine} =
472 0         0 $self->{templating_plugin}->new_engine($self, { template_dir => $doc_dir,
473             compile_dir => $doc_comp });
474            
475             # If the attributes doc_header, doc_footer, etc. were not set,
476             # check for the existence of defaults.
477            
478 0   0     0 my $doc_suffix = $self->{template_suffix} || '';
479            
480 0   0     0 $self->{doc_defs} //= $self->check_doc("doc_defs${doc_suffix}");
481 0   0     0 $self->{doc_header} //= $self->check_doc("doc_header${doc_suffix}");
482 0   0     0 $self->{doc_footer} //= $self->check_doc("doc_footer${doc_suffix}");
483 0   0     0 $self->{doc_default_template} //= $self->check_doc("doc_not_found${doc_suffix}");
484 0   0     0 $self->{doc_default_op_template} //= $self->check_doc("doc_op_template${doc_suffix}");
485             }
486            
487             # we were given a directory for output templates, initialize an
488             # engine for evaluating them as well.
489            
490 0 0       0 if ( $output_dir )
491             {
492 0 0       0 $output_dir = './' . $output_dir
493             unless $output_dir =~ qr{ ^ / }xs;
494            
495 0 0       0 croak "the output template directory '$output_dir' is not readable: $!\n"
496             unless -r $output_dir;
497            
498 0         0 $self->{output_template_dir} = $output_dir;
499            
500             $self->{output_engine} =
501 0         0 $self->{templating_plugin}->new_engine($self, { template_dir => $output_dir,
502             compile_dir => $output_comp });
503             }
504             }
505            
506             # Check and configure the backend plugin
507             # --------------------------------------
508            
509             # If a backend plugin was explicitly specified, check that it is valid.
510            
511 1 50 33     19 if ( my $backend_plugin = $self->backend_plugin )
    50 33        
512             {
513 0 0       0 eval "require $backend_plugin" or croak $@;
514            
515 0 0       0 croak "$backend_plugin is not a valid backend plugin: cannot find method 'get_connection'\n"
516             unless $backend_plugin->can('get_connection');
517             }
518            
519             # Otherwise, if 'Dancer::Plugin::Database' is available then select the
520             # corresponding plugin.
521            
522             elsif ( $INC{'Dancer.pm'} && $INC{'Dancer/Plugin/Database.pm'} && ! defined $self->backend_plugin )
523             {
524 0         0 $self->{backend_plugin} = 'Web::DataService::Plugin::Dancer';
525             }
526            
527             # Otherwise, we get the stub backend plugin which will throw an exception
528             # if called. If you still wish to access a backend data system, then you
529             # must either add code to the various operation methods to explicitly
530             # connect to it use one of the available hooks.
531            
532             else
533             {
534 1         3 $self->{backend_plugin} = 'Web::DataService::Plugin::Backend';
535             }
536            
537             # Let the backend plugin do whatever initialization it needs to.
538            
539 1         6 $self->_init_plugin('backend_plugin');
540            
541             # Register this instance so that we can select for it later
542             # ---------------------------------------------------------
543            
544 1         5 $self->_register_instance;
545            
546             # Check and set some attributes
547             # -----------------------------
548            
549             # The title must be non-empty, but we can't just label it 'required'
550             # because it might be specified in the configuration file.
551            
552 1         26 my $title = $self->title;
553            
554 1 50 33     13 croak "you must specify a title, either as a parameter to the data service definition or in the configuration file\n"
555             unless defined $title && $title ne '';
556            
557             # If no path_re was set, generate it from the path prefix.
558            
559 1 50       18 if ( ! $self->path_re )
560             {
561 1         15 my $prefix = $self->path_prefix;
562            
563             # If the prefix ends in '/', then generate a regexp that can handle
564             # either the prefix as given or the prefix string without the final /
565             # and without anything after it.
566            
567 1 50       14 if ( $prefix =~ qr{ (.*) [/] $ }xs )
568             {
569 0         0 $self->{path_re} = qr{ ^ [/] $1 (?: [/] (.*) | $ ) }xs;
570             }
571            
572             # Otherwise, generate a regexp that doesn't expect a / before the rest
573             # of the path.
574            
575             else
576             {
577 1         30 $self->{path_re} = qr{ ^ [/] $prefix (.*) }xs;
578             }
579             }
580            
581             # Create a default vocabulary, to be used in case no others are defined.
582            
583 1         11 $self->{vocab} = { 'null' =>
584             { name => 'null', use_field_names => 1, _default => 1, title => 'Null vocabulary',
585             doc_string => "This default vocabulary consists of the field names from the underlying data." } };
586            
587 1         4 $self->{vocab_list} = [ 'null' ];
588            
589             # We need to set defaults for 'doc_suffix' and 'index_name' so that we can
590             # handle 'doc_paths' if it is enabled. Application authors can turn
591             # either of these off by setting the value to the empty string.
592            
593 1   50     8 $self->{doc_suffix} //= '_doc';
594 1   50     7 $self->{doc_index} //= 'index';
595            
596             # Compute regexes from these suffixes.
597            
598 1 50 33     6 if ( $self->{doc_suffix} && $self->{doc_index} )
    0          
    0          
599             {
600 1         50 $self->{doc_path_regex} = qr{ ^ ( .* [^/] ) (?: $self->{doc_suffix} | / $self->{doc_index} | / ) $ }xs;
601             }
602            
603             elsif ( $self->{doc_suffix} )
604             {
605 0         0 $self->{doc_path_regex} = qr{ ^ ( .* [^/] ) (?: $self->{doc_suffix} | / ) $ }xs;
606             }
607            
608             elsif ( $self->{doc_index} )
609             {
610 0         0 $self->{doc_path_regex} = qr{ ^ ( .* [^/] ) (?: / $self->{doc_index} | / $ }xs;
611             }
612            
613             # Create a new HTTP::Validate object so that we can do parameter
614             # validations.
615            
616 1         14 $self->{validator} = HTTP::Validate->new();
617            
618             $self->{validator}->validation_settings(allow_unrecognized => 1)
619 1 50       25 unless $self->{feature}{strict_params};
620            
621             # Add a few other necessary fields.
622            
623 1         4 $self->{path_defs} = {};
624 1         4 $self->{node_attrs} = {};
625 1         3 $self->{attr_cache} = {};
626 1         3 $self->{format} = {};
627 1         3 $self->{format_list} = [];
628 1         2 $self->{subservice} = {};
629 1         9 $self->{subservice_list} = [];
630             }
631              
632              
633             # _init_value ( param )
634             #
635             # Return the initial value for the specified parameter. If it is already
636             # present as a direct attribute, return that. Otherwise, look it up in the
637             # hash of values from the configuration file. If those fail, check our parent
638             # (if we have a parent).
639              
640             sub _init_value {
641            
642 4     4   9 my ($self, $param) = @_;
643            
644 4 50 33     16 die "empty configuration parameter" unless defined $param && $param ne '';
645            
646             # First check to see if we have this attribute specified directly.
647             # Otherwise, check whether it is in our _config hash. Otherwise,
648             # if we have a parent then check its direct attributes and _config hash.
649             # Otherwise, return undefined.
650            
651 4         10 my $ds_name = $self->name;
652            
653 4 50       12 return $self->{$param} if defined $self->{$param};
654 4 50       10 return $self->{_config}{$ds_name}{$param} if defined $self->{_config}{$ds_name}{$param};
655 4 50       9 return $self->{parent}->_init_value($param) if defined $self->{parent};
656 4 50       9 return $self->{_config}{$param} if defined $self->{_config}{$param};
657            
658 4         29 return;
659             }
660              
661              
662             # _init_plugin ( plugin )
663             #
664             # If the specified plugin has an 'initialize_service' method, call it with
665             # ourselves as the argument.
666              
667             sub _init_plugin {
668              
669 1     1   3 my ($self, $plugin) = @_;
670            
671 1 50       4 return unless defined $self->{$plugin};
672            
673 2     2   7936 no strict 'refs';
  2         6  
  2         730  
674            
675 1 50 33     15 if ( $self->{$plugin}->can('initialize_plugin') && ! ${"$self->{$plugin}::_INITIALIZED"} )
  0         0  
676             {
677 0         0 $self->{$plugin}->initialize_plugin($self);
678 0         0 ${"$self->{$plugin}::_INITIALIZED"} = 1;
  0         0  
679             }
680            
681 1 50 33     11 if ( defined $self->{$plugin} && $self->{$plugin}->can('initialize_service') )
682             {
683 0         0 $self->{$plugin}->initialize_service($self);
684             }
685             }
686              
687              
688             # set_foundation ( plugin_module )
689             #
690             # Initialize the foundation plugin. If no name is given, try to determine the
691             # proper plugin based on the available modules.
692              
693             sub set_foundation {
694              
695 1     1 1 4 my ($self, $plugin_module) = @_;
696            
697             # If an argument is specified and the foundation framework has already
698             # been set, raise an exception.
699            
700 1 50 33     11 if ( defined $FOUNDATION && defined $plugin_module && $plugin_module ne $FOUNDATION )
    50 33        
    50          
701             {
702 0         0 croak "set_foundation: the foundation framework was already set to $FOUNDATION\n"
703             }
704            
705             # If a plugin module is specified, require it.
706            
707             elsif ( $plugin_module )
708             {
709 0 0       0 eval "require $plugin_module" or croak $@;
710            
711 0 0       0 croak "class '$plugin_module' is not a valid foundation plugin: cannot find method 'read_config'\n"
712             unless $plugin_module->can('read_config');
713            
714 0         0 $FOUNDATION = $plugin_module;
715             }
716            
717             # Otherwise, if 'Dancer.pm' has already been required then install the
718             # corresponding plugin.
719            
720             elsif ( $INC{'Dancer.pm'} )
721             {
722 1 50       448 require Web::DataService::Plugin::Dancer or croak $@;
723 1         4 $FOUNDATION = 'Web::DataService::Plugin::Dancer';
724             }
725            
726             # Checks for other foundation frameworks will go here.
727            
728             # Otherwise, we cannot proceed. Give the user some idea of what to do.
729            
730             else
731             {
732 0         0 croak "could not find a foundation framework: try installing Dancer and adding 'use Dancer;' to your application\n";
733             }
734            
735             # Now initialize the plugin.
736            
737 2     2   18 no strict 'refs';
  2         5  
  2         6449  
738            
739 1 50 33     14 if ( $FOUNDATION->can('initialize_plugin') && ! ${"${FOUNDATION}::_INITIALIZED"} )
  1         9  
740             {
741 1         6 $FOUNDATION->initialize_plugin();
742 1         70 ${"$FOUNDATION}::_INITIALIZED"} = 1;
  1         7  
743             }
744            
745 1 50 33     13 if ( ref $self eq 'Web::DataService' && $FOUNDATION->can('initialize_service') )
746             {
747 0         0 $FOUNDATION->initialize_service($self);
748             }
749             }
750              
751              
752             # config_value ( param )
753             #
754             # Return the value (if any) specified for this parameter in the configuration
755             # file. If not found, check the configuration for our parent (if we have a
756             # parent). This differs from _init_value above in that direct attributes are
757             # not checked.
758              
759             sub config_value {
760              
761 12     12 1 18 my ($self, $param) = @_;
762            
763 12 50 33     37 die "empty configuration parameter" unless defined $param && $param ne '';
764            
765             # First check to see whether this parameter is in our _config hash.
766             # Otherwise, if we have a parent then check its _config hash. Otherwise,
767             # return undefined.
768            
769 12         24 my $ds_name = $self->name;
770            
771 12 50       24 return $self->{_config}{$ds_name}{$param} if defined $self->{_config}{$ds_name}{$param};
772 12 50       25 return $self->{parent}->config_value($param) if defined $self->{parent};
773 12 50       21 return $self->{_config}{$param} if defined $self->{_config}{$param};
774            
775 12         21 return;
776             }
777              
778              
779             # has_feature ( name )
780             #
781             # Return true if the given feature is set for this data service, undefined
782             # otherwise.
783              
784             sub has_feature {
785            
786 0     0 1 0 my ($self, $name) = @_;
787            
788 0 0       0 croak "has_feature: unknown feature '$name'\n" unless $SPECIAL_FEATURE{$name};
789 0         0 return $self->{feature}{$name};
790             }
791              
792              
793             # special_param ( name )
794             #
795             # If the given special parameter is enabled for this data service, return the
796             # parameter name. Otherwise, return the undefined value.
797              
798             sub special_param {
799            
800 0     0 1 0 my ($self, $name) = @_;
801            
802 0 0       0 croak "special_param: unknown special parameter '$name'\n" unless $SPECIAL_PARAM{$name};
803 0         0 return $self->{special}{$name};
804             }
805              
806              
807             # valid_name ( name )
808             #
809             # Return true if the given name is valid according to the Web::DataService
810             # specification, false otherwise.
811              
812             sub valid_name {
813            
814 2     2 1 14 my ($self, $name) = @_;
815            
816 2 50 33     50 return 1 if defined $name && !ref $name && $name =~ qr{ ^ [\w][\w.:-]* $ }xs;
      33        
817 0         0 return; # otherwise
818             }
819              
820              
821             # _register_instance ( )
822             #
823             # Register this instance's key and path prefix so that the application code can
824             # later locate the appropriate service for handling each request.
825              
826             sub _register_instance {
827              
828 1     1   3 my ($self) = @_;
829            
830             # Add this to the list of defined data service instances.
831            
832 1         3 push @WDS_INSTANCES, $self;
833            
834             # If the attribute 'key' was defined, add it to the key map.
835            
836 1 50       21 if ( my $key = $self->key )
837             {
838             croak "You cannot register two data services with the key '$key'\n"
839 0 0       0 if $KEY_MAP{$key};
840            
841 0         0 $KEY_MAP{$key} = $self;
842             }
843            
844             # If the path prefix was defined, add it to the prefix map.
845            
846 1 50       20 if ( my $prefix = $self->path_prefix )
847             {
848 0 0 0     0 if ( defined $prefix && $prefix ne '' )
849             {
850 0         0 $PREFIX_MAP{$prefix} = $self;
851             }
852             }
853             }
854              
855              
856             # select ( outer )
857             #
858             # Return the data service instance that is appropriate for this request, or
859             # return an error if no instance could be matched. This should be called as a
860             # class method.
861              
862             sub select {
863            
864 0     0 0 0 my ($class, $outer) = @_;
865            
866 0         0 my $param;
867            
868             # Throw an error unless we have at least one data service instance to work with.
869            
870 0 0       0 croak "No data service instances have been defined" unless @WDS_INSTANCES;
871            
872 0         0 my $instance = $WDS_INSTANCES[0];
873            
874             # If the special parameter 'selector' is active, then we will use its
875             # value to determine the appropriate data service instance. We check the
876             # first instance defined because all instances in this application should
877             # either enable or disable this parameter alike.
878            
879 0 0       0 if ( $param = $instance->{special}{selector} )
880             {
881 0         0 my $key = $FOUNDATION->get_param($outer, $param);
882            
883             # If the parameter value matches a data service instance, return that.
884            
885 0 0 0     0 if ( defined $key && $KEY_MAP{$key} )
886             {
887 0         0 return $KEY_MAP{$key};
888             }
889            
890             # Otherwise, if the URL path is empty or just '/', return the first
891             # instance defined.
892            
893 0         0 my $path = $FOUNDATION->get_request_path($outer);
894            
895 0 0 0     0 if ( !defined $path || $path eq '' || $path eq '/' )
      0        
896             {
897 0         0 return $instance;
898             }
899            
900             # Otherwise, return an error message specifying the proper values.
901            
902 0         0 my @keys = sort keys %KEY_MAP;
903 0         0 my $good_values = join(', ', map { "v=$_" } @keys);
  0         0  
904            
905 0 0 0     0 if ( defined $key && $key ne '' )
906             {
907 0         0 die "400 Invalid version '$key' - you must specify one of the following parameters: $good_values\n";
908             }
909            
910             else
911             {
912 0         0 die "400 You must specify a data service version using one of the following parameters: $good_values\n";
913             }
914             }
915            
916             # Otherwise, check the request path against each data service instance to
917             # see if we can figure out which one to use by means of the regexes
918             # stored in the path_re attribute.
919            
920             else
921             {
922 0         0 my $path = $FOUNDATION->get_request_path($outer);
923            
924             # If one of the defined data service instances matches the path,
925             # return that.
926            
927 0         0 foreach my $ds ( @WDS_INSTANCES )
928             {
929 0 0 0     0 if ( defined $ds->{path_re} && $path =~ $ds->{path_re} )
930             {
931 0         0 return $ds;
932             }
933             }
934            
935             # If this is an OPTIONS request for '*', then return the first defined
936             # instance.
937            
938 0 0 0     0 if ( $FOUNDATION->get_http_method eq 'OPTIONS' && ($path eq '*' || $path eq '/*') )
      0        
939             {
940 0         0 return $WDS_INSTANCES[0];
941             }
942            
943             # Otherwise throw a 404 (Not Found) exception.
944            
945 0         0 my @prefixes = sort keys %PREFIX_MAP;
946 0         0 my $good_values = join(', ', map { "/$_" } @prefixes);
  0         0  
947            
948 0 0       0 if ( @prefixes > 1 )
    0          
949             {
950 0         0 die "404 The path '$path' is not valid. Try a path starting with one of the following: $good_values\n";
951             }
952            
953             elsif ( @prefixes == 1 )
954             {
955 0         0 die "404 The path '$path' is not valid. Try a path starting with $good_values\n";
956             }
957            
958             else
959             {
960 0         0 die "404 The path '$path' is not valid on this server.";
961             }
962             }
963             }
964              
965              
966             # get_connection
967             #
968             # Call the backend plugin to get a database connection for use by this data service.
969              
970             sub get_connection {
971            
972 0     0 1 0 my ($self) = @_;
973            
974             croak "get_connection: no backend plugin was loaded\n"
975 0 0       0 unless defined $self->{backend_plugin};
976 0         0 return $self->{backend_plugin}->get_connection($self);
977             }
978              
979              
980              
981             # set_mode ( mode... )
982             #
983             # Set one or more execution modes, which control how the data service processes requests and
984             # generates output. These modes are set for the entire process, so if multiple data services are
985             # defined they will share the same modes. All of these modes have been made available primarily
986             # for debugging and testing purposes. The available modes are as follows:
987             #
988             # debug Write debugging output to STDERR, including the text of any SQL
989             # statements issued to the database.
990             #
991             # quiet Suppress all output to STDERR, even important warning messages.
992             # This is useful mainly during unit tests.
993             #
994             # one_request This mode should be set when a request is specified via command-line
995             # arguments. It causes only those roles necessary for the specified
996             # request to be initialized, instead of all roles included in the data
997             # service configuration.
998             #
999             # diagnostic This mode should be set when diagnostic output is requested via the
1000             # the command line. It directs the diagnostic routines to be called
1001             # instead of the usual request-execution routines.
1002             #
1003             # one_process This does not have any effect on the Web::DataService code, but can
1004             # be checked by data operation modules. If it is set, then only one
1005             # process is assumed to be active at any one time. Consequently, certain
1006             # operations that would otherwise use temporary tables can use permanent
1007             # tables instead, which makes debugging easier.
1008             #
1009              
1010             sub set_mode {
1011            
1012 1     1 1 91 my ($self, @modes) = @_;
1013            
1014 1         4 foreach my $mode (@modes)
1015             {
1016 1 50       12 if ( $mode eq 'debug' )
    50          
    50          
    50          
    50          
    0          
1017             {
1018 0 0 0     0 $DEBUG = 1 unless $QUIET || $ENV{WDS_QUIET};
1019             }
1020            
1021             elsif ( $mode eq 'one_process' )
1022             {
1023 0         0 $ONE_PROCESS = 1;
1024             }
1025            
1026             elsif ( $mode eq 'one_request' )
1027             {
1028 0         0 $ONE_REQUEST = 1;
1029             }
1030            
1031             elsif ( $mode eq 'late_path_check' )
1032             {
1033 0         0 $CHECK_LATER = 1;
1034             }
1035            
1036             elsif ( $mode eq 'quiet' )
1037             {
1038 1         2 $QUIET = 1;
1039 1         4 $DEBUG = 0;
1040             }
1041            
1042             elsif ( $mode eq 'diagnostic' )
1043             {
1044 0         0 $DIAGNOSTIC = 1;
1045             }
1046             }
1047             }
1048              
1049              
1050             # is_mode ( mode )
1051             #
1052             # Returns true if the specified mode has been enabled, false otherwise.
1053              
1054             sub is_mode {
1055              
1056 1     1 1 4 my ($self, $mode) = @_;
1057            
1058 1 0 33     5 return 1 if $mode eq 'debug' && $DEBUG;
1059 1 0 33     4 return 1 if $mode eq 'one_request' && $ONE_REQUEST;
1060 1 0 33     4 return 1 if $mode eq 'late_path_check' && $CHECK_LATER;
1061 1 0 33     12 return 1 if $mode eq 'quiet' && $QUIET;
1062 1 50 33     7 return 1 if $mode eq 'diagnostic' && $DIAGNOSTIC;
1063 1         4 return;
1064             }
1065              
1066              
1067             # Specifically return true if 'debug' mode is enabled, false otherwise.
1068              
1069             sub debug_mode {
1070              
1071 0     0 0   my ($self) = @_;
1072            
1073 0           return $DEBUG;
1074             }
1075              
1076              
1077             sub debug {
1078              
1079 0     0 1   my ($self) = @_;
1080            
1081 0           return $DEBUG;
1082             }
1083              
1084              
1085             # Output a line of debugging information, if 'debug' mode is enabled.
1086              
1087             sub debug_line {
1088            
1089 0     0 1   my ($self, $msg) = @_;
1090            
1091 0 0         print STDERR "$msg\n" if $DEBUG;
1092             }
1093              
1094              
1095             sub debug_list {
1096            
1097 0 0   0 0   return unless $DEBUG;
1098            
1099 0           my ($self, @msgs) = @_;
1100            
1101 0           print STDERR "$_\n\n" foreach @msgs;
1102             }
1103              
1104              
1105             # generate_site_url ( attrs )
1106             #
1107             # Generate a URL according to the specified attributes:
1108             #
1109             # node Generates a documentation URL for the specified data service node
1110             #
1111             # op Generates an operation URL for the specified data service node
1112             #
1113             # path Generates a URL for this exact path (with the proper prefix added)
1114             #
1115             # format Specifies the format to be included in the URL
1116             #
1117             # params Species the parameters, if any, to be included in the URL
1118             #
1119             # fragment Specifies a fragment identifier to add to the generated URL
1120             #
1121             # type Specifies the type of URL to generate: 'abs' for an
1122             # absolute URL, 'rel' for a relative URL, 'site' for
1123             # a site-relative URL (starts with '/'). Defaults to 'site'.
1124              
1125             sub generate_site_url {
1126              
1127 0     0 1   my ($self, $attrs) = @_;
1128            
1129             # If the attributes were given as a string rather than a hash, unpack them.
1130            
1131 0 0 0       unless ( ref $attrs )
1132             {
1133 0 0 0       return '/' . $self->{path_prefix} unless defined $attrs && $attrs ne '' && $attrs ne '/';
      0        
1134            
1135 0 0         if ( $attrs =~ qr{ ^ (node|op|path) (abs|rel|site)? [:] ( [^#?]* ) (?: [?] ( [^#]* ) )? (?: [#] (.*) )? }xs )
1136             {
1137 0           my $arg = $1;
1138 0   0       my $type = $2 || 'site';
1139 0   0       my $path = $3 || '/';
1140 0           my $params = $4;
1141 0           my $frag = $5;
1142 0           my $format;
1143            
1144 0 0 0       if ( $arg ne 'path' && $path =~ qr{ (.*) [.] ([^.]+) $ }x )
1145             {
1146 0           $path = $1; $format = $2;
  0            
1147             }
1148            
1149 0           $attrs = { $arg => $path, type => $type, format => $format,
1150             params => $params, fragment => $frag };
1151             }
1152            
1153             else
1154             {
1155 0           return $attrs;
1156             }
1157             }
1158            
1159             elsif ( ref $attrs ne 'HASH' )
1160             {
1161             croak "generate_site_url: the argument must be a hashref or a string\n";
1162             }
1163            
1164             # If a custom routine was specified for this purpose, call it.
1165            
1166 0 0         if ( $self->{generate_url_hook} )
1167             {
1168 0           return &{$self->{generate_url_hook}}($self, $attrs);
  0            
1169             }
1170            
1171             # Otherwise, construct the URL according to the feature set of this data
1172             # service.
1173            
1174 0   0       my $path = $attrs->{node} || $attrs->{op} || $attrs->{path} || '';
1175 0           my $format = $attrs->{format};
1176 0   0       my $type = $attrs->{type} || 'site';
1177            
1178 0 0 0       unless ( defined $path )
1179             {
1180 0           carp "generate_site_url: you must specify a URL path\n";
1181             }
1182            
1183             elsif ( ! $attrs->{path} && $path =~ qr{ (.*) [.] ([^.]+) $ }x )
1184             {
1185             $path = $1;
1186             $format = $2;
1187             }
1188            
1189 0 0 0       $format = 'html' if $attrs->{node} && ! (defined $format && $format eq 'pod');
      0        
1190            
1191 0           my @params = ref $attrs->{params} eq 'ARRAY' ? @{$attrs->{params}}
1192             : defined $attrs->{params} ? split(/&/, $attrs->{params})
1193 0 0         : ();
    0          
1194            
1195 0           my ($has_format, $has_selector);
1196            
1197 0           foreach my $p ( @params )
1198             {
1199 0 0 0       $has_format = 1 if $self->{special}{format} && $p =~ qr{ ^ $self->{special}{format} = \S }x;
1200 0 0 0       $has_selector = 1 if $self->{special}{selector} && $p =~ qr{ ^ $self->{special}{selector} = \S }xo;
1201             }
1202            
1203             # if ( defined $attrs->{node} && ref $attrs->{node} eq 'ARRAY' )
1204             # {
1205             # push @params, @{$attrs->{node}};
1206             # croak "generate_url: odd number of parameters is not allowed\n"
1207             # if scalar(@_) % 2;
1208             # }
1209            
1210             # First, check if the 'fixed_paths' feature is on. If so, then the given
1211             # documentation or operation path is converted to a parameter and the appropriate
1212             # fixed path is substituted.
1213            
1214 0 0         if ( $self->{feature}{fixed_paths} )
1215             {
1216 0 0         if ( $attrs->{node} )
    0          
1217             {
1218 0 0         push @params, $self->{special}{document} . "=$path" unless $path eq '/';
1219 0           $path = $self->{doc_url_path};
1220             }
1221            
1222             elsif ( $attrs->{op} )
1223             {
1224 0           push @params, $self->{special}{op} . "=$path";
1225 0           $path = $self->{operation_url_path};
1226             }
1227             }
1228            
1229             # Otherwise, we can assume that the URL paths will reflect the given path.
1230             # So next, check if the 'format_suffix' feature is on.
1231            
1232 0 0         if ( $self->{feature}{format_suffix} )
    0          
1233             {
1234             # If this is a documentation URL, then add the documentation suffix if
1235             # the "doc_paths" feature is on. Also add the format. But not if the
1236             # path is '/'.
1237            
1238 0 0 0       if ( $attrs->{node} && $path ne '/' )
    0          
1239             {
1240 0 0         $path .= $self->{doc_suffix} if $self->{feature}{doc_paths};
1241 0           $path .= ".$format";
1242             }
1243            
1244             # If this is an operation URL, we just add the format if one was
1245             # specified.
1246            
1247             elsif ( $attrs->{op} )
1248             {
1249 0 0         $path .= ".$format" if $format;
1250             }
1251            
1252             # A path URL is not modified.
1253             }
1254            
1255             # Otherwise, if the feature 'doc_paths' is on then we still need to modify
1256             # the paths.
1257            
1258             elsif ( $self->{feature}{doc_paths} )
1259             {
1260 0 0 0       if ( $attrs->{node} && $path ne '/' )
1261             {
1262 0           $path .= $self->{doc_suffix};
1263             }
1264             }
1265            
1266             # If the special parameter 'format' is enabled, then we need to add it
1267             # with the proper format name.
1268            
1269 0 0 0       if ( $self->{special}{format} && ! $has_format && ! $attrs->{path} )
      0        
1270             {
1271             # If this is a documentation URL, then add a format parameter unless
1272             # the format is either 'html' or empty.
1273            
1274 0 0 0       if ( $attrs->{node} && $format && $format ne 'html' )
    0 0        
1275             {
1276 0           push @params, $self->{special}{format} . "=$format";
1277             }
1278            
1279             # If this is an operation URL, we add the format unless it is empty.
1280            
1281             elsif ( $attrs->{op} )
1282             {
1283 0 0         push @params, $self->{special}{format} . "=$format" if $format;
1284             }
1285            
1286             # A path URL is not modified.
1287             }
1288            
1289             # If the special parameter 'selector' is enabled, then we need to add it
1290             # with the proper data service key.
1291            
1292 0 0 0       if ( $self->{special}{selector} && ! $has_selector )
1293             {
1294 0           my $key = $self->key;
1295 0           push @params, $self->{special}{selector} . "=$key";
1296             }
1297            
1298             # If the path is '/', then turn it into the empty string.
1299            
1300 0 0         $path = '' if $path eq '/';
1301            
1302             # Now assemble the URL. If the type is not 'relative' then we start with
1303             # the path prefix. Otherwise, we start with the given path.
1304            
1305 0           my $url;
1306            
1307 0 0         if ( $type eq 'rel' )
    0          
1308             {
1309 0           $url = $path;
1310             }
1311            
1312             elsif ( $type eq 'abs' )
1313             {
1314 0           $url = $self->{base_url} . $self->{path_prefix} . $path;
1315             }
1316            
1317             else
1318             {
1319 0           $url = '/' . $self->{path_prefix} . $path;
1320             }
1321            
1322             # Add the parameters and fragment, if any.
1323            
1324 0 0         if ( @params )
1325             {
1326 0           $url .= '?';
1327 0           my $sep = '';
1328            
1329 0           while ( @params )
1330             {
1331 0           $url .= $sep . shift(@params);
1332 0           $sep = '&';
1333             }
1334             }
1335            
1336 0 0         if ( $attrs->{fragment} )
1337             {
1338 0           $url .= "#$attrs->{fragment}";
1339             }
1340            
1341             # Return the resulting URL.
1342            
1343 0           return $url;
1344             }
1345              
1346              
1347             # node_link ( path, title )
1348             #
1349             # Generate a link in POD format to the documentation for the given path. If
1350             # $title is defined, use that as the link title. Otherwise, if the path has a
1351             # 'doc_title' attribute, use that.
1352             #
1353             # If something goes wrong, generate a warning and return the empty string.
1354              
1355             sub node_link {
1356            
1357 0     0 0   my ($self, $path, $title) = @_;
1358            
1359 0 0         return 'I>' unless defined $path;
1360            
1361             # Generate a "node:" link for this path, which will be translated into an
1362             # actual URL later.
1363            
1364 0 0 0       if ( defined $title && $title ne '' )
    0          
1365             {
1366 0           return "L<$title|node:$path>";
1367             }
1368            
1369             elsif ( $title = $self->node_attr($path, 'title') )
1370             {
1371 0           return "L<$title|node:$path>";
1372             }
1373            
1374             else
1375             {
1376 0           return "I>";
1377             }
1378             }
1379              
1380              
1381             # base_url ( )
1382             #
1383             # Return the base URL for this data service, in the form "http://hostname/".
1384             # If the attribute 'port' was specified for this data service, include that
1385             # too.
1386              
1387             sub base_url {
1388            
1389 0     0 0   my ($self) = @_;
1390            
1391 0           carp "CALL: base_url\n";
1392            
1393             #return $FOUNDATION->get_base_url;
1394            
1395 0   0       my $hostname = $self->{hostname} // '';
1396 0 0         my $port = $self->{port} ? ':' . $self->{port} : '';
1397            
1398 0           return "http://${hostname}${port}/";
1399             }
1400              
1401              
1402             # root_url ( )
1403             #
1404             # Return the root URL for this data service, in the form
1405             # "http://hostname/prefix/".
1406              
1407             sub root_url {
1408              
1409 0     0 0   my ($self) = @_;
1410            
1411 0           carp "CALL: root_url\n";
1412            
1413             #return $FOUNDATION->get_base_url . $self->{path_prefix};
1414            
1415 0   0       my $hostname = $self->{hostname} // '';
1416 0 0         my $port = $self->{port} ? ':' . $self->{port} : '';
1417            
1418 0           return "http://${hostname}${port}/$self->{path_prefix}";
1419             }
1420              
1421              
1422             # execution_class ( primary_role )
1423             #
1424             # This method is called to create a class in which we can execute requests.
1425             # We need to create one of these for each primary role used in the
1426             # application.
1427             #
1428             # This class needs to have two roles composed into it: the first is
1429             # Web::DataService::Request, which provides methods for retrieving the request
1430             # parameters, output fields, etc.; the second is the "primary role", written
1431             # by the application author, which provides methods to implement one or more
1432             # data service operations. We cannot simply use Web::DataService::Request as
1433             # the base class, as different requests may require composing in different
1434             # primary roles. We cannot use the primary role as the base class, because
1435             # then any method conflicts would be resolved in favor of the primary role.
1436             # This would compromise the functionality of Web::DataService::Request, which
1437             # needs to be able to call its own methods reliably.
1438             #
1439             # The best way to handle this seems to be to create a new, empty class and
1440             # then compose in both the primary role and Web::DataService::Request using a
1441             # single 'with' request. This way, an exception will be thrown if the two
1442             # sets of methods conflict. This new class will be named using the prefix
1443             # 'REQ::', so that if the primary role is 'Example' then the new class will be
1444             # 'REQ::Example'.
1445             #
1446             # Any other roles needed by the primary role must also be composed in. We
1447             # also must check for an 'initialize' method in each of these roles, and call
1448             # it if present. As a result, we cannot simply rely on transitive composition
1449             # by having the application author use 'with' to include one role inside
1450             # another. Instead, the role author must indicate additional roles as
1451             # follows:
1452             #
1453             # package MyRole;
1454             # use Moo::Role;
1455             #
1456             # our(@REQUIRES_ROLE) = qw(SubRole1 SubRole2);
1457             #
1458             # Both the primary role and all required roles will be properly initialized,
1459             # which includes calling their 'initialize' method if one exists. This will
1460             # be done only once per role, no matter how many contexts it is used in. Each
1461             # of the subsidiary roles will be composed one at a time into the request
1462             # execution class.
1463              
1464             sub execution_class {
1465              
1466 0     0 0   my ($self, $primary_role) = @_;
1467            
1468 2     2   21 no strict 'refs';
  2         5  
  2         832  
1469            
1470 0 0 0       croak "you must specify a non-empty primary role"
1471             unless defined $primary_role && $primary_role ne '';
1472            
1473             croak "you must first load the module '$primary_role' before using it as a primary role"
1474 0 0 0       unless $primary_role eq 'DOC' || %{ "${primary_role}::" };
  0            
1475            
1476 0           my $request_class = "REQ::$primary_role";
1477            
1478             # $DB::single = 1;
1479            
1480             # First check to see if this class has already been created. Return
1481             # immediately if so.
1482            
1483 0 0         return $request_class if exists ${ "${request_class}::" }{_CREATED};
  0            
1484            
1485             # Otherwise create the new class and compose in Web::DataService::Request
1486             # and the primary role. Then compose in any secondary roles, one at a time.
1487            
1488 0           my $secondary_roles = "";
1489            
1490 0           foreach my $role ( @{ "${primary_role}::REQUIRES_ROLE" } )
  0            
1491             {
1492             croak "create_request_class: you must first load the module '$role' \
1493             before using it as a secondary role for '$primary_role'"
1494 0 0         unless %{ "${role}::" };
  0            
1495            
1496 0           $secondary_roles .= "with '$role';\n";
1497             }
1498            
1499 0           my $string = " package $request_class;
1500             # use Try::Tiny;
1501             # use Scalar::Util qw(reftype);
1502             # use Carp qw(carp croak);
1503             use Moo;
1504             use namespace::clean;
1505            
1506             use base 'Web::DataService::Request';
1507             with 'Web::DataService::IRequest', '$primary_role';
1508             $secondary_roles
1509            
1510             our(\$_CREATED) = 1";
1511            
1512 0           my $result = eval $string;
1513            
1514 0 0         if ( $@ )
1515             {
1516 0 0         if ( $@ =~ qr{method name conflict.*the method '(.*?)'} )
1517             {
1518 0           my $method = $1;
1519 0           croak "The method name '$method' in $primary_role conflicts with the same name in Web::DataService::IRequest; you must choose another name for this subroutine";
1520             }
1521            
1522             else
1523             {
1524 0           croak "$@";
1525             }
1526             }
1527            
1528             # Now initialize the primary role, unless of course it has already been
1529             # initialized. This will also cause any uninitialized secondary roles to
1530             # be initialized.
1531            
1532 0 0         $self->initialize_role($primary_role) unless $primary_role eq 'DOC';
1533            
1534 0           return $request_class;
1535             }
1536              
1537              
1538             # documentation_class ( primary_role )
1539             #
1540             # This method is called to create a class into which we can bless an object
1541             # that represents a documentation request. This will potentially be called
1542             # once for each different primary role in the data service application, plus
1543             # once to create a generic documentation class not based on any role.
1544             #
1545             # The classes created here must include all of the methods necessary for
1546             # generating documentation, including all of the methods in the indicated
1547             # role(s).
1548              
1549             sub documentation_class {
1550              
1551 0     0 0   my ($self, $primary_role) = @_;
1552            
1553 2     2   19 no strict 'refs';
  2         6  
  2         610  
1554            
1555             # First check to see if the necessary class has already been created.
1556             # Return immediately if so, because we have nothing left to do. If no
1557             # primary role was specified, the name of the class will be "DOC".
1558            
1559 0 0         my $request_class = $primary_role ? "DOC::$primary_role" : "DOC";
1560            
1561 0 0         return $request_class if exists ${ "${request_class}::" }{_CREATED};
  0            
1562            
1563             # Make sure that a package corresponding to the specified primary role
1564             # actually exists.
1565            
1566             croak "you must first load the module '$primary_role' before using it as a primary role"
1567 0 0 0       if $primary_role && ! %{ "${primary_role}::" };
  0            
1568            
1569             # If the primary role has not yet been initialized, do so. This will also
1570             # cause any uninitialized secondary roles to be initialized.
1571            
1572 0 0         $self->initialize_role($primary_role) if $primary_role;
1573            
1574             # Now create the new class and compose into it both
1575             # Web::DataService::Request and the primary role. By doing these together
1576             # we will generate an error if there are any method conflicts between
1577             # these packages. Also compose in any secondary roles, one at a time.
1578             # Any method conflicts here will be silently resolved in favor of the
1579             # primary role and/or Web::DataService::Request.
1580            
1581 0           my $primary_with = "";
1582 0           my $secondary_roles = "";
1583            
1584 0 0         if ( $primary_role )
1585             {
1586 0           $primary_with = ", '$primary_role'";
1587            
1588 0           foreach my $role ( @{ "${primary_role}::REQUIRES_ROLE" } )
  0            
1589             {
1590             croak "create_request_class: you must first load the module '$role' \
1591             before using it as a secondary role for '$primary_role'"
1592 0 0         unless %{ "${role}::" };
  0            
1593            
1594 0           $secondary_roles .= "with '$role';\n";
1595             }
1596             }
1597            
1598 0           my $string = " package $request_class;
1599             use Carp qw(carp croak);
1600             use Moo;
1601             use namespace::clean;
1602            
1603             use base 'Web::DataService::Request';
1604             with 'Web::DataService::IDocument' $primary_with;
1605             $secondary_roles
1606            
1607             our(\$_CREATED) = 1";
1608            
1609 0           my $result = eval $string;
1610            
1611 0           return $request_class;
1612             }
1613              
1614              
1615             # initialize_role ( role )
1616             #
1617             # This method calls the 'initialize' method of the indicated role, but first
1618             # it recursively processes every role required by that role. The intialize
1619             # method is only called once per role per execution of this program, no matter
1620             # how many contexts it is used in.
1621              
1622             sub initialize_role {
1623            
1624 0     0 0   my ($self, $role) = @_;
1625            
1626 2     2   17 no strict 'refs'; no warnings 'once';
  2     2   6  
  2         79  
  2         16  
  2         4  
  2         1228  
1627            
1628             # If we have already initialized this role, there is nothing else we need
1629             # to do.
1630            
1631 0 0         return if $self->{role_init}{$role};
1632 0           $self->{role_init}{$role} = 1;
1633            
1634             # If this role requires one or more secondary roles, then initialize them
1635             # first (unless they have already been initialized).
1636            
1637 0           foreach my $required ( @{ "${role}::REQUIRES_ROLE" } )
  0            
1638             {
1639 0           $self->initialize_role($required);
1640             }
1641            
1642             # Now, if the role has an initialization routine, call it. We need to do
1643             # this after the previous step because this role's initialization routine
1644             # may depend upon side effects of the required roles' initialization routines.
1645            
1646 0 0         if ( $role->can('initialize') )
1647             {
1648 0 0 0       print STDERR "Initializing $role for data service $self->{name}\n" if $DEBUG || $self->{DEBUG};
1649 0           $role->initialize($self);
1650             }
1651            
1652 0           my $a = 1; # we can stop here when debugging
1653             }
1654              
1655              
1656             # set_scratch ( key, value )
1657             #
1658             # Store the specified value in the "scratchpad" for this data service, under
1659             # the specified key. This can be used to store data, configuration
1660             # information, etc. for later use by data operation methods.
1661              
1662             sub set_scratch {
1663            
1664 0     0 0   my ($self, $key, $value) = @_;
1665            
1666 0 0 0       return unless defined $key && $key ne '';
1667            
1668 0           $self->{scratch}{$key} = $value;
1669             }
1670              
1671              
1672             # get_scratch ( key, value )
1673             #
1674             # Retrieve the value corresponding to the specified key from the "scratchpad" for
1675             # this data service.
1676              
1677             sub get_scratch {
1678            
1679 0     0 0   my ($self, $key, $value) = @_;
1680            
1681 0 0 0       return unless defined $key && $key ne '';
1682            
1683 0           return $self->{scratch}{$key};
1684             }
1685              
1686              
1687             # data_info ( )
1688             #
1689             # Return the following pieces of information:
1690             # - The name of the data source
1691             # - The license under which the data is made available
1692              
1693             sub data_info {
1694            
1695 0     0 0   my ($self) = @_;
1696            
1697 0           my $access_time = strftime("%a %F %T GMT", gmtime);
1698            
1699 0           my $title = $self->{title};
1700 0           my $data_provider = $self->data_provider;
1701 0           my $data_source = $self->data_source;
1702 0           my $data_license = $self->data_license;
1703 0           my $license_url = $self->license_url;
1704            
1705 0           my $result = {
1706             title => $title,
1707             data_provider => $data_provider,
1708             data_source => $data_source,
1709             data_license => $data_license,
1710             license_url => $license_url,
1711             access_time => $access_time };
1712            
1713 0           return $result;
1714             }
1715              
1716              
1717             # data_info_keys
1718             #
1719             # Return a list of keys into the data_info hash, in the proper order to be
1720             # listed in a response message.
1721              
1722             sub data_info_keys {
1723            
1724 0     0 0   return @DI_KEYS;
1725             }
1726              
1727              
1728             # contact_info ( )
1729             #
1730             # Return the data service attributes "contact_name" and "contact_email",
1731             # as a hash whose keys are "name" and "email".
1732              
1733             sub contact_info {
1734            
1735 0     0 0   my ($self) = @_;
1736            
1737 0           my $result = {
1738             name => $self->contact_name,
1739             email => $self->contact_email };
1740            
1741 0           return $result;
1742             }
1743              
1744              
1745             # get_base_path ( )
1746             #
1747             # Return the base path for the current data service, derived from the path
1748             # prefix. For example, if the path prefix is 'data', the base path is
1749             # '/data/'.
1750              
1751             # sub get_base_path {
1752            
1753             # my ($self) = @_;
1754            
1755             # my $base = '/';
1756             # $base .= $self->{path_prefix} . '/'
1757             # if defined $self->{path_prefix} && $self->{path_prefix} ne '';
1758            
1759             # return $base;
1760             # }
1761              
1762              
1763             =head1 MORE DOCUMENTATION
1764              
1765             This documentation describes the methods of class Web::DataService. For
1766             additional documentation, see the following pages:
1767              
1768             =over
1769              
1770             =item L
1771              
1772             A description of the request-handling process, along with detailed
1773             documentation of the methods that can be called with request objects.
1774              
1775             =item L
1776              
1777             A detailed description of this module and its reasons for existence.
1778              
1779             =item L
1780              
1781             A step-by-step guide to the example application included with this
1782             distribution.
1783              
1784             =item L
1785              
1786             A detailed description of how to configure a data service using this
1787             framework. This page includes sub-pages for each different type of data
1788             service element.
1789              
1790             =item L
1791              
1792             An overview of the elements available for use in documentation templates.
1793              
1794             =item L
1795              
1796             Features that are available for debugging your application.
1797              
1798             =back
1799              
1800             =head1 METHODS
1801              
1802             =head2 CONSTRUCTOR
1803              
1804             =head3 new ( { attributes ... } )
1805              
1806             This class method defines a new data service instance. Calling it is generally the first step in configuring
1807             a data service application. The available attributes are described in
1808             L. The attribute C is required; the
1809             others are optional, and some of them may be specified in the application configuration file instead.
1810              
1811             Once you have a data service instance, the next step is to configure it by adding various data service
1812             elements. This is done by calling the methods listed below.
1813              
1814             =head2 CONFIGURATION
1815              
1816             The following methods are used to configure a data service application. For a list of the available
1817             attributes for each method, and an overview of the calling convention, see
1818             L. For detailed instructions on how to set up a data service application,
1819             see L.
1820              
1821             =head3 set_foundation ( module_name )
1822              
1823             You can call this as a class method if you wish to use a custom foundation
1824             framework. The argument must be the module name, which will be require'd.
1825             This call must occur before any data services are defined.
1826              
1827             =head3 define_vocab ( { attributes ... }, documentation ... )
1828              
1829             Defines one or more
1830             L, using the
1831             specified attributes and documentation strings. Each vocabulary represents a
1832             different set of terms by which to label and express the returned data.
1833              
1834             =head3 define_format ( { attributes ... }, documentation ... )
1835              
1836             Defines one or more L,
1837             using the specified attributes and documentation strings. Each of these
1838             formats represents a configuration of one of the available serialization
1839             modules.
1840              
1841             =head3 define_node ( { attributes ... }, documentation ... )
1842              
1843             Defines one or more L
1844             nodes|Web::DataService::Configuration::Node>, using the specified attributes
1845             and documentation strings. Each of these nodes represents either an operation
1846             provided by the data service or a page of documentation.
1847              
1848             =head3 list_node ( { attributes ... }, documentation ... )
1849              
1850             Adds one or more entries to a L,
1851             which can be used to document lists of related nodes. You can use this to
1852             document node relationships that are not strictly hierarchical.
1853              
1854             =head3 define_block ( block_name, { attributes ... }, documentation ... )
1855              
1856             Defines an L with the
1857             given name, containing the specified output fields and documentation.
1858              
1859             =head3 define_set ( set_name, { attributes ... }, documentation ... )
1860              
1861             Defines a named L,
1862             possibly with a mapping to some other list of values. These can be used to
1863             specify the acceptable values for request parameters, to translate data values
1864             into different vocabularies, or to specify optional output blocks.
1865              
1866             =head3 define_output_map ( set_name, { attributes ... }, documentation ... )
1867              
1868             This method is an alias for C.
1869              
1870             =head3 define_ruleset ( ruleset_name, { attributes ... }, documentation ... )
1871              
1872             Defines a L with
1873             the given name, containing the specified rules and documentation. These are
1874             used to validate parameter values.
1875              
1876             =head2 EXECUTION
1877              
1878             The following methods are available for you to use in the part of your code
1879             that handles incoming requests. This will typically be inside one or more
1880             "route handlers" or "controllers" defined using the foundation framework.
1881              
1882             =head3 handle_request ( outer, [ attrs ] )
1883              
1884             A call to this method directs the Web::DataService framework to handle the
1885             current L. Depending on how your
1886             application is configured, one of the data service operation methods that you
1887             have written may be called as part of this process.
1888              
1889             You may call this either as a class method or an instance method. In the
1890             former case, if you have defined more than one data service instance, the
1891             method will choose the appropriate instance based on either the path prefix or
1892             selector parameter depending upon which features and special parameters you
1893             have enabled. If you know exactly which instance is the appropriate one, you
1894             may instead call this method on it directly.
1895              
1896             The first argument must be the "outer" request object, i.e. the one generated by
1897             the foundation framework. This allows the Web::DataService code to obtain
1898             details about the request and to compose the response using the functionality
1899             provided by that framework. This method will create an "inner" object in a
1900             subclass of L, with attributes derived from the
1901             current request and from the data service node that matches it. If no data
1902             service node matches the current request, a 404 error response will be
1903             returned to the client.
1904              
1905             You may provide a second optional argument, which must be a hashref of request
1906             attributes (see
1907             L).
1908             These will be used to initialize the request object, overriding any
1909             automatically determined attributes.
1910              
1911             This method returns the result of the request (generally the body of the
1912             response message), unless an error occurs. In the latter case an exception
1913             will be thrown, so your main application should include an appropriate handler
1914             to generate a proper error response. See the file
1915             L|Web::DataService::Tutorial/"lib/Example.pm"> in the
1916             tutorial example for more about this.
1917              
1918             =head3 new_request ( outer, [ attrs ] )
1919              
1920             If you wish more control over the request-handling process than is provided by
1921             L, you may instead call
1922             this method. It returns an object blessed into a subclass of
1923             Web::DataService::Request, as described above for C, but does
1924             not execute it.
1925              
1926             You can then examine and possibly alter any of the request attributes, before
1927             calling the request's C method. This method may, like
1928             C, be called either as a class method or an instance method.
1929              
1930             =head3 execute_request ( request )
1931              
1932             This method may be called to execute a request. The argument must belong to a
1933             subclass of L, created by a previous call to
1934             L. This method may, like
1935             C, be called either as a class method or an instance method.
1936              
1937             =head3 node_attr ( path, attribute )
1938              
1939             Returns the specified attribute of the node with the specified path, if the
1940             specified path and attribute are both defined. Returns C otherwise.
1941             You can use this to test whether a particular node is in fact defined, or to
1942             retrieve any node attribute.
1943              
1944             You will rarely need to call this method, since for any request the relevant
1945             attributes of the matching node will be automatically used to instantiate the
1946             request object. In almost all cases, you will instead use the attribute
1947             accessor methods of the request object.
1948              
1949             =head3 config_value ( name )
1950              
1951             Returns the value (if any) specified for this name in the application
1952             configuration file. If the name is found as a sub-entry under the data
1953             service name, that value is used. Otherwise, if the name is found as a
1954             top-level entry then it is used.
1955              
1956             =head3 has_feature ( feature_name )
1957              
1958             Returns a true value if the specified
1959             L
1960             is enabled for this data service. Returns false otherwise.
1961              
1962             =head3 special_param ( parameter_name )
1963              
1964             If the specified
1965             L
1966             [inst]"> is enabled for this data service, returns the parameter name which
1967             clients use. This may be different from the internal name by which this
1968             parameter is known, but will always be a true value. Returns false if this
1969             parameter is not enabled.
1970              
1971             =head3 generate_site_url ( attrs )
1972              
1973             This method is called by the
1974             L method of
1975             L. You should be aware that if you call it outside
1976             of the context of a request it will not be able to generate absolute URLs. In
1977             most applications, you will never need to call this directly and can instead
1978             use the latter method. The argument must be a hash reference, and the accepted
1979             keys are listed in the documentation for the latter method.
1980              
1981             =head3 get_connection
1982              
1983             If a backend plugin is available, this method obtains a connection handle from
1984             it. You can use this method when initializing your operation roles, if your
1985             initialization process requires communication with the backend. You are not
1986             required to use this mechanism, and may connect to the backend in any way you
1987             choose.
1988              
1989             =head3 set_mode
1990              
1991             Turn on one of the following modes, which are provided primarily for testing and debugging
1992             purposes. These modes are set for the entire process, not for a single data service.
1993              
1994             =over
1995              
1996             =item debug
1997              
1998             Debugging statements will be written to STDERR. Any data service operation code that you write
1999             should check for this mode being set, and if it is then call C to write out
2000             appropriate debugging information including the text of any SQL statements issued to the database.
2001              
2002             =item quiet
2003              
2004             All output to STDERR is suppressed, even important warning messages. This is useful mainly during
2005             unit tests, where error conditions are explicitly generated to check that the proper response
2006             occurs.
2007              
2008             =item one_request
2009              
2010             This mode should be set when a request is specified via command-line arguments. It causes only
2011             those roles necessary for the specified request to be initialized, instead of all roles included
2012             in the data service configuration.
2013              
2014             =item diagnostic
2015              
2016             This mode should be set when diagnostic output is requested via the command line. It directs the
2017             diagnostic routines to be called using the information specified on the command line, rather than
2018             the usual request-handling routines.
2019              
2020             =item one_process
2021              
2022             This mode should be set when a single data service process is run for debugging purposes. It has
2023             no effect on the Web::DataService code, but data operation code can check for it and use permanent
2024             tables rather than temporary ones, which can make debugging easier.
2025              
2026             =back
2027              
2028             =head3 is_mode ( mode )
2029              
2030             Returns true if the specified mode has been enabled for this data service process (not just for
2031             this data service). Returns false otherwise.
2032              
2033             =head3 debug
2034              
2035             Returns true if 'debug' mode has been enabled for this process (not just for this data service).
2036              
2037             =head3 debug_line ( text )
2038              
2039             If 'debug' mode is enabled, prints out the specified text followed by a newline to STDERR.
2040              
2041             =head3 accessor methods
2042              
2043             Each of the data service
2044             L
2045             is provided with an accessor method. This method returns the attribute value,
2046             but cannot be used to set it. All data service attributes must be set when
2047             the data service object is instantiated with C, either specified
2048             directly in that call or looked up in the application configuration file
2049             provided by the foundation framework.
2050              
2051             =head2 DOCUMENTATION
2052              
2053             The following methods are used in generating documentation. If you use
2054             documentation templates, you will probably not need to call them directly.
2055              
2056             =head3 document_vocabs ( path, { options ... } )
2057              
2058             Returns a documentation string in Pod for the
2059             L that are allowed
2060             for the node corresponding to the specified path. The optional C hash
2061             may include the following:
2062              
2063             =over
2064              
2065             =item all
2066              
2067             If this option has a true value then all vocabularies are documented, not just
2068             those allowed for the given path.
2069              
2070             =item extended
2071              
2072             If this option has a true value then the documentation string is included for
2073             each vocabulary.
2074              
2075             =back
2076              
2077             =head3 document_formats ( path, { options ... } )
2078              
2079             Return a string containing documentation in Pod for the
2080             L that are allowed for the
2081             node corresponding to the specified path. The optional C hash may
2082             include the following:
2083              
2084             =over
2085              
2086             =item all
2087              
2088             If this option has a true value then all formats are documented, not just
2089             those allowed for the given path.
2090              
2091             =item extended
2092              
2093             If this option has a true value then the documentation string is included for
2094             each format.
2095              
2096             =back
2097              
2098             =head3 document_nodelist ( list, { options ... } )
2099              
2100             Returns a string containing documentation in Pod for the specified
2101             L.
2102             Each node has a default node list whose name is its node path, and you can
2103             define other lists arbitrarily by using the method L.
2104             The optional C hash may include the following:
2105              
2106             =over
2107              
2108             =item usage
2109              
2110             If this documentation string has a non-empty value, then usage examples will
2111             be included if they are specified in the node list entries. The value of this
2112             attribute will be included in the result between each node's documentation
2113             string and its usage list, so it should be a string such as "For example:".
2114              
2115             =back
2116              
2117             =head2 MISCELLANEOUS
2118              
2119             =head3 valid_name ( name )
2120              
2121             Returns true if the given string is valid as a Web::DataService name. This
2122             means that it begins with a word character and includes only word characters
2123             plus the punctuation characters ':', '-' and '.'.
2124              
2125             =head3 set_mode ( mode ... )
2126              
2127             You can call this either as a class method or as an instance method; it has
2128             a global effect either way. This method turns on one or more of the
2129             following modes:
2130              
2131             =over 4
2132              
2133             =item debug
2134              
2135             Produces additional debugging output to STDERR.
2136              
2137             =item one_request
2138              
2139             Configures the data service to satisfy one request and then exit. This is
2140             generally used for testing purposes.
2141              
2142             =back
2143              
2144             You will typically call this at application startup time.
2145              
2146             =head1 AUTHOR
2147              
2148             mmcclenn "at" cpan.org
2149              
2150             =head1 BUGS
2151              
2152             Please report any bugs or feature requests to C, or through
2153             the web interface at L. I will be notified, and then you'll
2154             automatically be notified of progress on your bug as I make changes.
2155              
2156             =head1 COPYRIGHT & LICENSE
2157              
2158             Copyright 2014 Michael McClennen, all rights reserved.
2159              
2160             This program is free software; you can redistribute it and/or modify it
2161             under the same terms as Perl itself.
2162              
2163             =cut
2164              
2165              
2166             package Web::DataService::Plugin::Templating;
2167              
2168 2     2   28 use Carp qw(croak);
  2         6  
  2         272  
2169              
2170 0     0     sub render_template { croak "render_template: no templating plugin was specified\n"; }
2171              
2172              
2173             package Web::DataService::Plugin::Backend;
2174              
2175 2     2   16 use Carp qw(croak);
  2         15  
  2         183  
2176              
2177 0     0     sub get_connection { croak "get_connection: no backend plugin was specified"; }
2178              
2179              
2180             1;