Web-DataService/files/lib/Example.pm
#
# Example Data Service
#
# This file provides the base application for an example data service implemented
# using the Web::DataService framework.
#
# You can use it as a starting point for setting up your own data service.
#
# Author: Michael McClennen <mmcclenn@cpan.org>
use strict;
package Example;
use lib '../lib'; # Required for testing. Remove from production app.
use Dancer ':syntax'; # This module is required for
# Web::DataService, until plugins for other
# foundation frameworks are written.
#use Dancer::Plugin::Database; # You can uncomment this if you wish to use a
# backend database via DBI (the example
# application does not need it).
eval { require Template; }; # This is required in order to generate
# documentation pages.
use Web::DataService; # Bring in Web::DataService.
use PopulationData; # Load the code which will implement the
# data service operations for this
# application. If you use the current file as
# a basis for your own application, replace
# this line with your own module or modules.
# If we were called from the command line with at least one argument,
# then assume that we have been called for debugging purposes. The second
# argument should be a URL path.
if ( defined $ARGV[0] )
{
my $cmd = lc $ARGV[0];
# If the first command-line argument specifies an HTTP method
# (i.e. 'get') then set Dancer's apphandler to 'Debug'. This will
# cause Dancer to process a single request using the command-line
# arguments and then exit.
# In this case, the second argument must be the route path. The third
# argument if given should be a query string of the form
# 'param=value¶m=value...'. This should be quoted, to prevent
# interpretation by the command shell. Any subsequent arguments should be
# of the form 'var=value' and are used to set environment variables that
# would otherwise be set by Plack from HTTP request headers.
if ( $cmd eq 'get' || $cmd eq 'head' || $cmd eq 'put' || $cmd eq 'post' || $cmd eq 'delete' )
{
set apphandler => 'Debug';
set logger => 'console';
set show_errors => 0;
Web::DataService->set_mode('debug', 'one_request');
}
# If the first command-line argument is 'diag' then set a flag to indicate
# that Web::DataService should print out information about the
# configuration of this data service application and then exit. This
# function can be used to debug the configuration. The second argument
# should specify which operation path to request information about;
# specify the root path for the data service if you want information about
# the entire configuration.
# This option is deliberately made available only via the command-line
# for security reasons.
elsif ( $cmd eq 'diag' )
{
set apphandler => 'Debug';
set logger => 'console';
set show_errors => 0;
set startup_info => 0;
Web::DataService->set_mode('diagnostic');
# We need to alter the first argument to 'get' so that the Dancer
# routing algorithm will recognize it.
$ARGV[0] = 'GET';
}
# Otherwise, if the command-line argument is 'debug' then we run in
# the regular mode (accepting requests from a network port) but put
# Web::DataService into debug mode. This will cause debugging output
# to be printed to STDERR for eqch requests.
elsif ( $cmd eq 'debug' )
{
Web::DataService->set_mode('debug');
}
else
{
die "Unknown option '$cmd'\n";
}
}
# We begin by instantiating a data service object.
my $ds = Web::DataService->new(
{ name => 'data1.0',
title => 'Example Data Service',
features => 'standard',
special_params => 'standard',
path_prefix => 'data1.0/' });
# Continue by defining some output formats. These are automatically handled
# by the plugins Web::DataService::Plugin::JSON and
# Web::DataService::Plugin::Text.
$ds->define_format(
{ name => 'json', doc_node => 'formats/json', title => 'JSON' },
"The JSON format is intended primarily to support client applications.",
{ name => 'txt', doc_node => 'formats/text', title => 'Plain text' },
"The plain text format is intended for direct responses to humans,",
"or for loading into a spreadsheet. Data is formatted as lines of",
"comma-separated values.",
{ name => 'csv', doc_node => 'formats/text', title => 'Comma Separated Values' },
"The csv text format is identical to plain text, except that ",
"most browsers will offer to save it as a download",
{ name => 'tsv', doc_node => 'formats/text', title => 'Tab Separated Values' },
"The tsv text format returns data as lines of tab-separated values.",
"Most browsers will offer to save it as a download.");
# We then define a hierarchy of data service nodes. These nodes define the
# operations and documentation pages that will be available to users of this
# service. The node '/' defines a set of root attributes that will be
# inherited by all other nodes.
$ds->define_node({ path => '/',
title => 'Main Documentation',
public_access => 1,
default_format => 'json',
doc_default_template => 'doc_not_found.tt',
doc_default_op_template => 'operation.tt',
output => 'basic' });
# Any URL path starting with /css indicates a stylesheet file:
$ds->define_node({ path => 'css',
file_dir => 'css' });
# Some example operations:
$ds->define_node(
{ path => 'single',
title => 'Single state',
place => 1,
usage => [ "single.json?state=wi",
"single.txt?state=tx&show=hist&header=no" ],
output => 'basic',
optional_output => 'extra',
role => 'PopulationData',
method => 'single' },
"Returns information about a single U.S. state.",
{ path => 'list',
title => 'Multiple states',
place => 2,
usage => [ "list.json?region=ne&show=total",
"list.txt?region=we&show=hist,total&count&datainfo" ],
output => 'basic',
optional_output => 'extra',
role => 'PopulationData',
method => 'list' },
"Returns information about all of the states matching specified criteria.",
{ path => 'regions',
title => 'Regions',
place => 3,
usage => "regions.txt",
output => 'regions',
role => 'PopulationData',
method => 'regions' },
"Returns the list of region codes used by this data set.");
# Add documentation about the various output formats, parameters, etc..
$ds->define_node(
{ path => 'formats',
title => 'Output formats' },
{ path => 'formats/json',
title => 'JSON format' },
{ path => 'formats/text',
title => 'Plain text formats' },
{ path => 'special',
title => 'Special parameters' });
# Next we configure the Dancer routes that will allow this application to
# respond to various URL paths. For this simple example, all we
# need is a single route with which to capture all requests.
# This may be all you need even for more complicated applications. But if the
# node structure of Web::DataService is not sufficient to properly describe
# your application, you are free to add additional routes to process
# certain URLs differently.
any qr{.*} => sub {
return Web::DataService->handle_request(request);
};
# If an error occurs, we want to generate a Web::DataService response rather
# than the default Dancer response. In order for this to happen, we need the
# following two hooks:
hook on_handler_exception => sub {
var(error => $_[0]);
};
hook after_error_render => sub {
$ds->error_result(var('error'), var('wds_request'));
};
1;