File Coverage

blib/lib/Articulate/Service.pm
Criterion Covered Total %
statement 34 55 61.8
branch 0 6 0.0
condition 0 3 0.0
subroutine 10 14 71.4
pod 2 3 66.6
total 46 81 56.7


line stmt bran cond sub pod time code
1             package Articulate::Service;
2 11     11   50 use strict;
  11         14  
  11         337  
3 11     11   40 use warnings;
  11         14  
  11         249  
4              
5 11     11   2126 use Articulate::Syntax;
  11         21  
  11         58  
6              
7             # The following provide objects which must be created on a per-request basis
8 11     11   8941 use Articulate::Request;
  11         19  
  11         153  
9 11     11   2972 use Articulate::Response;
  11         19  
  11         41  
10              
11 11     11   2606 use Moo;
  11         18  
  11         59  
12              
13             with 'Articulate::Role::Service';
14 11     11   9659 use Try::Tiny;
  11         10729  
  11         541  
15 11     11   56 use Scalar::Util qw(blessed);
  11         16  
  11         405  
16              
17 11     11   51 use Exporter::Declare;
  11         16  
  11         62  
18             default_exports qw(articulate_service);
19              
20             =head1 NAME
21              
22             Articulate::Service - provide an API to all the core Articulate
23             features.
24              
25             =cut
26              
27             =head1 DESCRIPTION
28              
29             The Articulate Service provides programmatic access to all the core
30             features of the Articulate app. It is the intermediary between the
31             B and all other B.
32              
33             Mostly, you will want to be calling the service in routes, for
34             instance:
35              
36             get 'zone/:zone_name/article/:article_name' => sub {
37             my ($zone_name, $article_name) = param('zone_name'), param('article_name');
38             return $self->process_request ( read => "/zone/$zone_name/article/$article_name' )
39             }
40              
41             However, you may also want to call it from one-off scripts, tests,
42             etc., especially where you want to perform tasks which you don't want
43             to make available in routes, or where you are already in a perl
44             environment and mapping to the HTTP layer would be a distraction. In
45             theory you could create an application which did not have any web
46             interface at all using this service, e.g. a command-line app on a
47             shared server.
48              
49             =cut
50              
51             sub articulate_service {
52 0     0 0 0 __PACKAGE__->new(@_);
53             }
54              
55             has providers => (
56             is => 'rw',
57             default => sub { [] },
58             coerce => sub { instantiate_array(@_) },
59             );
60              
61             =head3 process_request
62              
63             my $response = service->process_request($request);
64             my $response = service->process_request($verb => $data);
65              
66             This is the primary method of the service: Pass in an
67             Articulate::Request object and the Service will produce a Response
68             object to match.
69              
70             Alternatively, if you pass a string as the first argument, the request
71             will be created from the verb and the data.
72              
73             Which verbs are handled, what data they require, and how they will be
74             processed are determined by the service providers you have set up in
75             your config: C passes the request to each of the
76             providers in turn and asks them to process the request.
77              
78             Providers can decline to process the request by returning undef, which
79             will cause the service to offer the requwst to the next provider.
80              
81             Note that a provider MAY act on a request and still return undef, e.g.
82             to perform logging, however it is discouraged to perform acctions which
83             a user would typically expect a response from (e.g. a create action
84             should return a response and not just pass to a get to confirm it has
85             successfully created what it was suppsed to).
86              
87             =cut
88              
89             sub process_request {
90 0     0 1 0 my $self = shift;
91 0         0 my @underscore = @_; # because otherwise the try block will eat it
92 0         0 my $request;
93 0         0 my $response = new_response error => {
94             error => Articulate::Error::NotFound->new(
95             { simple_message => 'No appropriate Service Provider found' }
96             )
97             };
98             try {
99 0 0   0   0 if ( ref $underscore[0] ) {
100 0         0 $request = $underscore[0];
101             }
102             else { # or accept $verb => $data
103 0         0 $request = new_request(@underscore);
104             }
105 0         0 foreach my $provider ( @{ $self->providers } ) {
  0         0  
106 0         0 $provider->app( $self->app );
107 0         0 my $resp = $provider->process_request($request);
108 0 0       0 if ( defined $resp ) {
109 0         0 $response = $resp;
110 0         0 last;
111             }
112             }
113             }
114             catch {
115 0     0   0 local $@ = $_;
116 0 0 0     0 if ( blessed $_ and $_->isa('Articulate::Error') ) {
117 0         0 $response = new_response error => { error => $_ };
118             }
119             else {
120 0         0 $response =
121             new_response error => { error =>
122             Articulate::Error->new( { simple_message => 'Unknown error' . $@ } )
123             };
124             }
125 0         0 };
126 0         0 return $response;
127             }
128              
129             =head3 enumerate_verbs
130              
131             my @verbs = @{ $self->enumerate_verbs };
132              
133             Returns an arrayref of verbs which at list one provider will process.
134              
135             =cut
136              
137             sub enumerate_verbs {
138 1     1 1 28 my $self = shift;
139 1         2 my $verbs = {};
140 1         2 foreach my $provider ( @{ $self->providers } ) {
  1         3  
141 5         933 $verbs->{$_}++ foreach keys %{ $provider->verbs };
  5         15  
142             }
143 1         35 return [ sort keys %$verbs ];
144             }
145              
146             =head1 SEE ALSO
147              
148             =over
149              
150             =item * L
151              
152             =item * L
153              
154             =item * L
155              
156             =back
157              
158             =cut
159              
160             1;