File Coverage

blib/lib/McBain.pm
Criterion Covered Total %
statement 166 167 99.4
branch 53 60 88.3
condition 27 36 75.0
subroutine 20 21 95.2
pod n/a
total 266 284 93.6


line stmt bran cond sub pod time code
1             package McBain;
2              
3             # ABSTRACT: Framework for building portable, auto-validating and self-documenting APIs
4              
5 4     4   21639 use warnings;
  4         7  
  4         118  
6 4     4   13 use strict;
  4         7  
  4         87  
7              
8 4     4   2018 use Brannigan;
  4         19281  
  4         99  
9 4     4   23 use Carp;
  4         6  
  4         346  
10 4     4   19 use File::Spec;
  4         4  
  4         79  
11 4     4   14 use Scalar::Util qw/blessed/;
  4         3  
  4         393  
12 4     4   2114 use Try::Tiny;
  4         4285  
  4         932  
13              
14             our $VERSION = "2.001000";
15             $VERSION = eval $VERSION;
16              
17             =head1 NAME
18            
19             McBain - Framework for building portable, auto-validating and self-documenting APIs
20              
21             =head1 SYNOPSIS
22              
23             package MyAPI;
24              
25             use McBain; # imports strict and warnings for you
26              
27             get '/multiply' => (
28             description => 'Multiplies two integers',
29             params => {
30             one => { required => 1, integer => 1 },
31             two => { required => 1, integer => 1 }
32             },
33             cb => sub {
34             my ($api, $params) = @_;
35              
36             return $params->{one} * $params->{two};
37             }
38             );
39              
40             post '/factorial' => (
41             description => 'Calculates the factorial of an integer',
42             params => {
43             num => { required => 1, integer => 1, min_value => 0 }
44             },
45             cb => sub {
46             my ($api, $params) = @_;
47              
48             # note how this route both uses another
49             # route and calls itself recursively
50              
51             if ($params->{num} <= 1) {
52             return 1;
53             } else {
54             return $api->forward('GET:/multiply', {
55             one => $params->{num},
56             two => $api->forward('POST:/factorial', { num => $params->{num} - 1 })
57             });
58             }
59             }
60             );
61              
62             1;
63              
64             =head1 DESCRIPTION
65              
66             C is a framework for building powerful APIs and applications. Writing an API with C provides the following benefits:
67              
68             =over
69              
70             =item * B
71              
72             C is extremely lightweight, with minimal dependencies on non-core modules; only two packages; and a succinct, minimal syntax that is easy to remember. Your APIs and applications will require less resources and perform better. Maybe.
73              
74             =item * B
75              
76             C APIs can be run/used in a variety of ways with absolutely no changes of code. For example, they can be used B (see L), as fully fledged B (see L), as B (see L), or as B (see L). Seriously, no change of code required. More L are yet to come (plus search CPAN to see if more are available), and you can
77             easily create your own, god knows I don't have the time or motivation or talent. Why should I do it
78             for you anyway?
79              
80             =item * B
81              
82             No more tedious input tests. C will handle input validation for you. All you need to do is define the parameters you expect to get with the simple and easy to remember syntax provided by L. When your API is used, C will automatically validate input. If validation fails, C will return appropriate errors and tell the users of your API that they suck.
83              
84             =item * B
85              
86             C also eases the burden of having to document your APIs, so that other people can actually use it (and you, two weeks later when you're drunk and can't remember why you wrote the thing in the first place). Using simple descriptions you give to your API's methods, and the parameter definitions, C can automatically create a manual document describing your API (see the L command line utility).
87              
88             =item * B
89              
90             APIs written with C are modular and flexible. You can make them object oriented if you want, or not, C won't care, it's unobtrusive like that. APIs are hierarchical, and every module in the API can be used as a complete API all by itself, detached from its siblings, so you can actually load only the parts of the API you need. Why is this useful? I don't know, maybe it isn't, what do I care? It happened by accident anyway.
91              
92             =item * B
93              
94             It'll do that too, just give it a chance.
95              
96             =back
97              
98             =head1 FUNCTIONS
99              
100             The following functions are exported:
101              
102             =head2 provide( $method, $route, %opts )
103              
104             Define a method and a route. C<$method> is one of C, C, C
105             or C. C<$route> is a string that starts with a forward slash,
106             like a path in a URI. C<%opts> can hold the following keys (only C
107             is required):
108              
109             =over
110              
111             =item * description
112              
113             A short description of the method and what it does.
114              
115             =item * params
116              
117             A hash-ref of parameters in the syntax of L (see L
118             for a complete references).
119              
120             =item * cb
121              
122             An anonymous subroutine (or a subroutine reference) to run when the route is
123             called. The method will receive the root topic class (or object, if the
124             topics are written in object oriented style), and a hash-ref of parameters.
125              
126             =back
127              
128             =head2 get( $route, %opts )
129              
130             Shortcut for C
131              
132             =head2 post( $route, %opts )
133              
134             Shortcut for C
135              
136             =head2 put( $route, %opts )
137              
138             Shortcut for C
139              
140             =head2 del( $route, %opts )
141              
142             Shortcut for C
143              
144             =head2 pre_route( $cb->( $self, $meth_and_route, \%params ) )
145              
146             =head2 post_route( $cb->( $self, $meth_and_route, \$ret ) )
147              
148             Define a post_route method to run before/after every request to a route in the
149             defining topic. See L for details.
150              
151             =head1 METHODS
152              
153             The following methods will be available on importing classes/objects:
154              
155             =head2 call( @args )
156              
157             Calls the API, requesting the execution of a certain route. This is the
158             main way your API is used. The arguments it expects to receive and its
159             behavior are dependent on the L used. Refer to the docs
160             of the runner you wish to use for more information.
161              
162             =head2 forward( $namespace, [ \%params ] )
163              
164             For usage from within API methods; this simply calls a method of the
165             the API with the provided parameters (if any) and returns the result.
166             With C, an API method can call other API methods or even
167             itself (for recursive operations).
168              
169             C<$namespace> is the method and route to execute, in the format C<< : >>,
170             where C is one of C, C, C, C, and C
171             starts with a forward slash.
172              
173             =head2 is_root( )
174              
175             Returns a true value if the module is the root topic of the API.
176             Mostly used internally and in L modules.
177              
178             =cut
179              
180             our %INFO;
181              
182             sub import {
183 7     7   68 my $target = caller;
184 7 100       33 return if $target eq 'main';
185 6         6 my $me = shift;
186 6         58 strict->import;
187 6         97 warnings->import(FATAL => 'all');
188 6 50       10 return if $INFO{$target};
189              
190             # find the root of this API (if it's not this class)
191 6         12 my $root = _find_root($target);
192              
193             # create the routes hash for $root
194 6   100     25 $INFO{$root} ||= {};
195              
196             # were there any options passed?
197 6 100       12 if (scalar @_) {
198 3         5 my %opts = map { s/^-//; $_ => 1 } @_;
  3         10  
  3         10  
199             # apply the options to the root package
200 3         7 $INFO{$root}->{_opts} = \%opts;
201             }
202              
203             # figure out the topic name from this class
204 6         9 my $topic = '/';
205 6 100       11 unless ($target eq $root) {
206 3         34 my $rel_name = ($target =~ m/^${root}::(.+)$/)[0];
207 3         7 $topic = '/'.lc($rel_name);
208 3         7 $topic =~ s!::!/!g;
209             }
210              
211 4     4   22 no strict 'refs';
  4         5  
  4         6589  
212              
213             # export the is_root() subroutine to the target topic,
214             # so that it knows whether it is the root of the API
215             # or not
216 6         26 *{"${target}::is_root"} = sub {
217 0     0   0 exists $INFO{$target};
218 6         24 };
219              
220 6 100       15 if ($target eq $root) {
221 3         11 *{"${target}::import"} = sub {
222 3     3   36 my $t = caller;
223 3         3 shift;
224 3 50       8 my $runner = scalar @_ ? 'McBain::'.ucfirst(substr($_[0], 1)) : 'McBain::Directly';
225              
226 3         134 eval "require $runner";
227 3 50       15 croak "Can't load runner module $runner: $@"
228             if $@;
229              
230 3         12 $INFO{$root}->{_runner} = $runner;
231              
232             # let the runner module do needed initializations,
233             # as the init method usually needs the is_root subroutine,
234             # this statement must come after exporting is_root()
235 3         16 $runner->init($target);
236 3         9 };
237             }
238              
239             # export the provide subroutine to the target topic,
240             # so that it can define routes and methods.
241 6         18 *{"${target}::provide"} = sub {
242 20     20   36 my ($method, $name) = (shift, shift);
243 20         39 my %opts = @_;
244              
245             # make sure the route starts and ends
246             # with a slash, and prefix it with the topic
247 20 50       62 $name = '/'.$name
248             unless $name =~ m{^/};
249 20 100       52 $name .= '/'
250             unless $name =~ m{/$};
251 20 100       34 $name = $topic.$name
252             unless $topic eq '/';
253              
254 20   50     86 $INFO{$root}->{$name} ||= {};
255 20         51 $INFO{$root}->{$name}->{$method} = \%opts;
256 6         17 };
257              
258             # export shortcuts to the provide() subroutine
259             # per http methods
260 6         18 foreach my $meth (
261             [qw/get GET/],
262             [qw/put PUT/],
263             [qw/post POST/],
264             [qw/del DELETE/]
265             ) {
266 24         74 *{$target.'::'.$meth->[0]} = sub {
267 20     20   118 &{"${target}::provide"}($meth->[1], @_);
  20         52  
268 24         44 };
269             }
270              
271 6         8 my $forward_target = $target;
272              
273 6 100 100     36 if ($target eq $root && $INFO{$root}->{_opts} && $INFO{$root}->{_opts}->{contextual}) {
      66        
274             # we're running in contextual mode, which means the API
275             # should have a Context class called $root::Context, and this
276             # is the class to which we should export the forward() method
277             # (the call() method is still exported to the API class).
278             # when call() is, umm, called, we need to create a new instance
279             # of the context class and use forward() on it to handle the
280             # request.
281             # we expect this class to be called $root::Context, but if it
282             # does not exist, we will try going up the hierarchy until we
283             # find one.
284 2         4 my $check = $root.'::Context';
285 2         2 my $ft;
286 2         4 while ($check) {
287 4         185 eval "require $check";
288 4 100       370 if ($@) {
289             # go up one level and try again
290 2         15 $check =~ s/[^:]+::Context$/Context/;
291             } else {
292 2         3 $ft = $check;
293 2         4 last;
294             }
295             }
296              
297 2 50       5 croak "No context class found"
298             unless $ft;
299 2 50       19 croak "Context class doesn't have create_from_env() method"
300             unless $ft->can('create_from_env');
301              
302 2         4 $forward_target = $ft;
303             }
304              
305             # export the pre_route and post_route "constructors"
306 6         8 foreach my $mod (qw/pre_route post_route/) {
307 12         47 *{$target.'::'.$mod} = sub (&) {
308 3   100 3   20 $INFO{$root}->{"_$mod"} ||= {};
309 3         7 $INFO{$root}->{"_$mod"}->{$topic} = shift;
310 12         31 };
311             }
312              
313             # export the call method, the one that actually
314             # executes API methods
315 6         20 *{"${target}::call"} = sub {
316 30     30   13738 my ($self, @args) = @_;
317              
318 30         82 my $runner = $INFO{$root}->{_runner};
319              
320             return try {
321             # ask the runner module to generate a standard
322             # env hash-ref
323 30     30   4538 my $env = $runner->generate_env(@args);
324              
325 30 100 66     162 my $ctx = $INFO{$root}->{_opts} && $INFO{$root}->{_opts}->{contextual} ?
326             $forward_target->create_from_env($runner, $env, @args) :
327             $self;
328              
329             # handle the request
330 30         223 my $res = $ctx->forward($env->{METHOD}.':'.$env->{ROUTE}, $env->{PAYLOAD});
331              
332             # ask the runner module to generate an appropriate
333             # response with the result
334 19         200 return $runner->generate_res($env, $res);
335             } catch {
336             # an exception was caught, ask the runner module
337             # to format it as it needs
338 11     11   576 my $exp;
339 11 100 66     101 if (ref $_ && ref $_ eq 'HASH' && exists $_->{code} && exists $_->{error}) {
      66        
      33        
340 9         10 $exp = $_;
341             } else {
342 2         8 $exp = { code => 500, error => $_ };
343             }
344              
345 11         45 return $runner->handle_exception($exp, @args);
346 30         233 };
347 6         19 };
348              
349             # export the forward method, which is both used internally
350             # in call(), and can be used by API authors within API
351             # methods
352 6         20 *{"${forward_target}::forward"} = sub {
353 46     46   134 my ($ctx, $meth_and_route, $payload) = @_;
354              
355 46         105 my ($meth, $route) = split(/:/, $meth_and_route);
356              
357             # make sure route ends with a slash
358 46 100       144 $route .= '/'
359             unless $route =~ m{/$};
360              
361 46         44 my @captures;
362              
363             # is there a direct route that equals the request?
364 46         84 my $r = $INFO{$root}->{$route};
365              
366             # if not, is there a regex route that does?
367 46 100       83 unless ($r) {
368 9         10 foreach (keys %{$INFO{$root}}) {
  9         56  
369 113 100       1326 next unless @captures = ($route =~ m/^$_$/);
370 4         15 $r = $INFO{$root}->{$_};
371 4         9 last;
372             }
373             }
374              
375 46 100       165 confess { code => 404, error => "Route $route not found" }
376             unless $r;
377              
378             # is this an OPTIONS request?
379 41 100       83 if ($meth eq 'OPTIONS') {
380 1         2 my %options;
381 1         4 foreach my $m (keys %$r) {
382 1         2 %{$options{$m}} = map { $_ => $r->{$m}->{$_} } grep($_ ne 'cb', keys(%{$r->{$m}}));
  1         6  
  2         6  
  1         6  
383             }
384 1         4 return \%options;
385             }
386              
387             # does this route have the HTTP method?
388 40 100       156 confess { code => 405, error => "Method $meth not available for route $route" }
389             unless exists $r->{$meth};
390              
391             # process parameters
392 38         183 my $params_ret = Brannigan::process({ params => $r->{$meth}->{params} }, $payload);
393              
394 38 100       5728 confess { code => 400, error => "Parameters failed validation", rejects => $params_ret->{_rejects} }
395             if $params_ret->{_rejects};
396              
397             # break the path into "directories", run pre_route methods
398             # for each directory (if any)
399 36         76 my @parts = _break_path($route);
400              
401             # are there pre_routes?
402 36         66 foreach my $part (@parts) {
403 102 100 100     613 $INFO{$root}->{_pre_route}->{$part}->($ctx, $meth_and_route, $params_ret)
404             if $INFO{$root}->{_pre_route} && $INFO{$root}->{_pre_route}->{$part};
405             }
406              
407 34         143 my $res = $r->{$meth}->{cb}->($ctx, $params_ret, @captures);
408              
409             # are there post_routes?
410 34         152 foreach my $part (@parts) {
411 99 100 100     452 $INFO{$root}->{_post_route}->{$part}->($ctx, $meth_and_route, \$res)
412             if $INFO{$root}->{_post_route} && $INFO{$root}->{_post_route}->{$part};
413             }
414              
415 34         120 return $res;
416 6         28 };
417              
418             # we're done with exporting, now lets try to load all
419             # child topics (if any), and collect their method definitions
420 6         27 _load_topics($target, $INFO{$root}->{_opts});
421             }
422              
423             # _find_root( $current_class )
424             # -- finds the root topic of the API, which might
425             # very well be the module we're currently importing into
426              
427             sub _find_root {
428 6     6   5 my $class = shift;
429              
430 6         5 my $copy = $class;
431 6         27 while ($copy =~ m/::[^:]+$/) {
432 6 100       21 return $`
433             if $INFO{$`};
434 3         11 $copy = $`;
435             }
436              
437 3         9 return $class;
438             }
439              
440             # _load_topics( $base, [ \%opts ] )
441             # -- finds and loads the child topics of the class we're
442             # currently importing into, automatically requiring
443             # them and thus importing McBain into them as well
444              
445             sub _load_topics {
446 6     6   12 my ($base, $opts) = @_;
447              
448             # this code is based on code from Module::Find
449              
450 6         64 my $pkg_dir = File::Spec->catdir(split(/::/, $base));
451              
452 6         12 my @inc_dirs = map { File::Spec->catdir($_, $pkg_dir) } @INC;
  60         193  
453              
454 6         11 foreach my $inc_dir (@inc_dirs) {
455 60 100       1632 next unless -d $inc_dir;
456              
457 3         76 opendir DIR, $inc_dir;
458 3 100       84 my @pms = grep { !-d && m/\.pm$/ } readdir DIR;
  11         83  
459 3         22 closedir DIR;
460              
461 3         5 foreach my $file (@pms) {
462 4         17 my $pkg = $file;
463 4         20 $pkg =~ s/\.pm$//;
464 4         40 $pkg = join('::', File::Spec->splitdir($pkg));
465              
466 4         19 my $req = File::Spec->catdir($inc_dir, $file);
467              
468 4 50 66     31 next if $req =~ m!/Context.pm$!
      66        
469             && $opts && $opts->{contextual};
470              
471 3         1225 require $req;
472             }
473             }
474             }
475              
476             # _break_path( $path )
477             # -- breaks a route/path into a list of "directories",
478             # starting from the root and up to the full path
479              
480             sub _break_path {
481 36     36   51 my $path = shift;
482              
483 36         46 my $copy = $path;
484              
485 36         28 my @path;
486              
487 36 100       91 unless ($copy eq '/') {
488 35         48 chop($copy);
489              
490 35         74 while (length($copy)) {
491 68         99 unshift(@path, $copy);
492 68         334 $copy =~ s!/[^/]+$!!;
493             }
494             }
495              
496 36         48 unshift(@path, '/');
497              
498 36         119 return @path;
499             }
500              
501             =head1 MANUAL
502              
503             =head2 ANATOMY OF AN API
504              
505             Writing an API with C is easy. The syntax is short and easy to remember,
506             and the feature list is just what it needs to be - short and sweet.
507              
508             The main idea of a C API is this: a client requests the execution of a
509             method provided by the API, sending a hash of parameters. The API then executes the
510             method with the client's parameters, and produces a response. Every L
511             will enforce a different response format (and even request format). When the API is
512             L, for example, whatever the API produces is returned as
513             is. The L and L runners,
514             however, are both JSON-in JSON-out interfaces.
515              
516             A C API is built of one or more B, in a hierarchical structure.
517             A topic is a class that provides methods that are categorically similar. For
518             example, an API might have a topic called "math" that provides math-related
519             methods such as add, multiply, divide, etc.
520              
521             Since topics are hierarchical, every API will have a root topic, which may have
522             zero or more child topics. The root topic is where your API begins, and it's your
523             decision how to utilize it. If your API is short and simple, with methods that
524             cannot be categorized into different topics, then the entire API can live within the
525             root topic itself, with no child topics at all. If, however, you're building a
526             larger API, then the root topic might be empty, or it can provide general-purpose
527             methods that do not particularly fit in a specific topic, for example maybe a status
528             method that returns the status of the service, or an authentication method.
529              
530             The name of a topic is calculated from the name of the package itself. The root
531             topic is always called C (forward slash), and its child topics are named
532             like their package names, in lowercase, relative to the root topic, with C
533             as a separator instead of Perl's C<::>, and starting with a slash.
534             For example, lets look at the following API packages:
535              
536             +------------------------+-------------------+------------------+
537             | Package Name | Topic Name | Description |
538             +========================+===================+==================+
539             | MyAPI | "/" | the root topic |
540             | MyAPI::Math | "/math" | a child topic |
541             | MyAPI::Math::Constants | "/math/constants" | a child-of-child |
542             | MyAPI::Strings | "/strings" | a child topic |
543             +------------------------+--------------------------------------+
544              
545             You will notice that the naming of the topics is similar to paths in HTTP URIs.
546             This is by design, since I wrote C mostly for writing web applications
547             (with the L runner), and the RESTful architecture fits
548             well with APIs whether they are HTTP-based or not.
549              
550             =head2 CREATING TOPICS
551              
552             To create a topic package, all you need to do is:
553              
554             use McBain;
555              
556             This will import C functions into the package, register the package
557             as a topic (possibly the root topic), and attempt to load all child topics, if there
558             are any. For convenience, C will also import L and L for
559             you.
560              
561             Notice that using C doesn't make your package an OO class. If you want your
562             API to be object oriented, you are free to form your classes however you want, for
563             example with L or L:
564              
565             package MyAPI;
566              
567             use McBain;
568             use Moo;
569              
570             has 'some_attr' => ( is => 'ro' );
571              
572             1;
573              
574             =head2 CREATING ROUTES AND METHODS
575              
576             The resemblance with HTTP continues as we delve further into methods themselves. An API
577             topic defines B, and one or more B that can be executed on every
578             route. Just like HTTP, these methods are C, C, C and C.
579              
580             Route names are like topic names. They begin with a slash, and every topic I
581             have a root route which is just called C. Every method defined on a route
582             will have a complete name (or path, if you will), in the format
583             C<< : >>. For example, let's say we have a
584             topic called C, and this topic has a route called C, with one
585             C method defined on this route. The complete name (or path) of this method
586             will be C.
587              
588             By using this structure and semantics, it is easy to create CRUD interfaces. Lets
589             say your API has a topic called C, that deals with articles in your
590             blog. Every article has an integer ID. The C topic can have the following
591             routes and methods:
592              
593             +------------------------+--------------------------------------+
594             | Namespace | Description |
595             +========================+======================================+
596             | POST:/articles/ | Create a new article (root route /) |
597             | GET:/articles/(\d+) | Read an article |
598             | PUT:/articles/(\d+) | Update an article |
599             | DELETE:/articles/(\d+) | Delete an article |
600             +------------------------+--------------------------------------+
601              
602             Methods are defined using the L, L,
603             L and L subroutines.
604             The syntax is similar to L's antlers:
605              
606             get '/multiply' => (
607             description => 'Multiplies two integers',
608             params => {
609             a => { required => 1, integer => 1 },
610             b => { required => 1, integer => 1 }
611             },
612             cb => sub {
613             my ($api, $params) = @_;
614              
615             return $params->{a} * $params->{b};
616             }
617             );
618              
619             Of the three keys above (C, C and C), only C
620             is required. It takes the actual subroutine to execute when the method is
621             called. The subroutine will get two arguments: first, the root topic (either
622             its package name, or its object, if you're creating an object oriented API),
623             and a hash-ref of parameters provided to the method (if any).
624              
625             You can provide C with a short C of the method, so that
626             C can use it when documenting the API with L.
627              
628             You can also tell C which parameters your method takes. The C
629             key will take a hash-ref of parameters, in the format defined by L
630             (see L for a complete references). These will be both
631             enforced and documented.
632              
633             As you may have noticed in the C example, routes can be defined using
634             regular expressions. This is useful for creating proper RESTful URLs:
635              
636             # in topic /articles
637              
638             get '/(\d+)' => (
639             description => 'Returns an article by its integer ID',
640             cb => sub {
641             my ($api, $params, $id) = @_;
642              
643             return $api->db->get_article($id);
644             }
645             );
646              
647             If the regular expression contains L, and
648             a call to the API matches the regular expressions, the values captured will
649             be passed to the method, after the parameters hash-ref (even if the method
650             does not define parameters, in which case the parameters hash-ref will be
651             empty - this may change in the future).
652              
653             It is worth understanding how C builds the regular expression. In the
654             above example, the topic is C, and the route is C. Internally,
655             the generated regular expression will be C<^/articles/(\d+)$>. Notice how the topic
656             and route are concatenated, and how the C<^> and C<$> metacharacters are added to
657             the beginning and end of the regex, respectively. This means it is impossible to
658             create partial regexes, which only pose problems in my experience.
659              
660             =head2 OPTIONS REQUESTS
661              
662             Every route defined by the API also automatically gets an C method,
663             again just like HTTP. This method returns a list of HTTP-style methods allowed
664             on the route. The return format depends on the runner module used. The direct
665             runner will return a hash-ref with keys being the HTTP methods, and values being
666             hash-refs holding the C and C definitions (if any).
667              
668             For example, let's look at the following route:
669              
670             get '/something' => (
671             description => 'Gets something',
672             cb => sub { }
673             );
674              
675             put '/something' => (
676             description => 'Updates something',
677             params => { new_content => { required => 1 } },
678             cb => sub { }
679             );
680              
681             Calling C will return:
682              
683             {
684             GET => {
685             description => "Gets something"
686             },
687             PUT => {
688             description => "Updates something",
689             params => {
690             new_content => { required => 1 }
691             }
692             }
693             }
694              
695             =head2 CALLING METHODS FROM WITHIN METHODS
696              
697             Methods are allowed to call other methods (whether in the same route or not),
698             and even call themselves recursively. This can be accomplished easily with
699             the L method. For example:
700              
701             get '/factorial => (
702             description => 'Calculates the factorial of a number',
703             params => {
704             num => { required => 1, integer => 1 }
705             },
706             cb => sub {
707             my ($api, $params) = @_;
708              
709             if ($params->{num} <= 1) {
710             return 1;
711             } else {
712             return $api->forward('GET:/multiply', {
713             one => $params->{num},
714             two => $api->forward('GET:/factorial', { num => $params->{num} - 1 })
715             });
716             }
717             }
718             );
719              
720             In the above example, notice how the C method calls both
721             C and itself.
722              
723             =head2 EXCEPTIONS
724              
725             C APIs handle errors in a graceful way, returning proper error
726             responses to callers. As always, the way errors are returned depends on
727             the L used. When used directly from Perl
728             code, McBain will L (i.e. die) with a hash-ref consisting
729             of two keys:
730              
731             =over
732              
733             =item * C - An HTTP status code indicating the type of the error (for
734             example 404 if the route doesn't exist, 405 if the route exists but the method
735             is not allowed, 400 if parameters failed validation, etc.).
736              
737             =item * C - The text/description of the error.
738              
739             =back
740              
741             Depending on the type of the error, more keys might be added to the exception.
742             For example, the parameters failed validation error will also include a C
743             key holding L's standard rejects hash, describing which parameters failed
744             validation.
745              
746             When writing APIs, you are encouraged to return exceptions in this format to
747             ensure proper handling by C. If C encounters an exception
748             that does not conform to this format, it will generate an exception with
749             C 500 (indicating "Internal Server Error"), and the C key will
750             hold the exception as is.
751              
752             =head2 PRE-ROUTES AND POST-ROUTES
753              
754             I
755              
756             Every topic in your API can define pre and post routes. The pre route is called
757             right before a route is executed, while the post route is called immediately after.
758              
759             You should note that the pre and post routes are called on every route execution
760             (when applicable), even when forwarding from one route to another.
761              
762             Pre and post routes are hierarchical. When a route is executed, C will analyze
763             the entire chain of topics leading up to that route, and execute all pre and post routes
764             on the way (if any, of course). So, for example, if the route C is to be
765             executed, C will look for pre and post routes and the root topic (C), the C
766             topic, and the C topic (if it exists). Whichever ones it finds will be
767             executed, in order.
768              
769             The C subroutine gets as parameters the API package (or object, if writing
770             object-oriented APIs, or the context object, if writing in L),
771             the full route name (the method and the path, e.g. C), and the
772             parameters hash-ref, after validation has occurred.
773              
774             package MyApi::Math;
775              
776             post '/factorial' => (
777             ...
778             );
779              
780             pre_route {
781             my ($self, $meth_and_route, $params) = @_;
782              
783             # do something here
784             }
785              
786             The C subroutine gets the same parameters, except the parameters hash-ref, in which
787             place a reference to the result returned by the actual route is passed. So, for example, if
788             the C method returned C<13>, then C will get a reference
789             to a scalar variable whose value is 13.
790              
791             post_route {
792             my ($self, $meth_and_route, $ret) = @_;
793              
794             if ($$ret == 13) {
795             # change the result to 14, because
796             # 13 is an unlucky number
797             $$ret = 14;
798             }
799             }
800              
801             =head2 CONTEXTUAL MODE
802              
803             I<< B contextual mode is an experimental feature introduced in v1.2.0 and
804             may change in the future. >>
805              
806             Contextual mode is an optional way of writing C APIs, reminiscent of
807             web application frameworks such as L and L. The main idea
808             is that a context object is created for every request, and follows it during
809             its entire life.
810              
811             In regular mode, the API methods receive the class of the root package (or its
812             object, if writing object oriented APIs), and a hash-ref of parameters. This is
813             okay for simple APIs, but many APIs need more, like information about the
814             user who sent the request.
815              
816             In contextual mode, the context object can contain user information, methods for
817             checking authorization (think role-based and ability-based authorization systems),
818             database connections, and anything else your API might need in order to fulfill the
819             request.
820              
821             Writing APIs in contextual mode is basically the same as in regular mode, only you
822             need to build a context class. Since C doesn't intrude on your OO system of
823             choice, constructing the class is your responsibility, and you can use whatever you
824             want (like L, L, L). C only requires your
825             context class to implement a subroutine named C.
826             This method will receive the name of the runner module used, the standard environment
827             hash-ref of C (which includes the keys C, C and C),
828             plus all of the arguments that were sent to the L method. These are
829             useful for certain runner modules, such as the L,
830             which gets the L hash-ref, from which you can extract session data, user
831             information, HTTP headers, etc. Note that this means that if you plan to use your API
832             with different runner modules, your C method should be able to parse
833             differently formatted arguments.
834              
835             Note that currently, the context class has to be named C<__ROOT__::Context>, where
836             C<__ROOT__> is the name of your API's root package. So, for example, if your API's
837             root package is named C, then C will expect C.
838              
839             I<< B since v2.1.0, if C doesn't find a package named C<__ROOT__::Context>,
840             it will go up the package hierarchy until it finds one. For example, if the root package
841             of your API is C, then McBain will try C, then C,
842             then finally C. This was added to allow the sharing of the same context class
843             in a project comprised of several APIs. >>
844              
845             When writing in contextual mode, your API methods will receive the context object
846             instead of the root package/object, and the parameters hash-ref.
847              
848             Let's look at a simple example for writing APIs in contextual mode. Say our API
849             is called C. Let's begin with the context class, C:
850              
851             package MyAPI::Context;
852              
853             use Moo;
854             use Plack::Request;
855              
856             has 'user_agent' => (
857             is => 'ro',
858             default => sub { 'none' }
859             );
860              
861             sub create_from_env {
862             my ($class, $runner, $mcbain_env, @call_args) = @_;
863              
864             my $user_agent;
865              
866             if ($runner eq 'McBain::WithPSGI') {
867             # extract user agent from the PSGI env,
868             # which will be the first item in @call_args
869             $user_agent = Plack::Request->new($call_args[0])->user_agent;
870             }
871              
872             return $class->new(user_agent => $user_agent);
873             }
874              
875             1;
876              
877             Now let's look at the API itself:
878              
879             package MyAPI;
880              
881             use McBain -contextual;
882              
883             get '/' => (
884             cb => sub {
885             my ($c, $params) = @_;
886              
887             if ($c->user_agent =~ m/Android/) {
888             # do it this way
889             } else {
890             # do it that way
891             }
892              
893             # you can still forward to other methods
894             $c->forward('GET:/something_else', \%other_params);
895             }
896             );
897              
898             1;
899              
900             So as you can see, the only real change for API packages is the need
901             to write C instead of C. The only
902             "challenge" is writing the context class.
903              
904             =head1 MCBAIN RUNNERS
905              
906             I<< B since v2.0.0 the way runner modules are used has changed. The
907             C environment variable is no longer used. Read on for more
908             information. >>
909              
910             A runner module is in charge of loading C APIs in a specific way.
911             The default runner, L, is the simplest runner there is,
912             and is meant for using APIs directly from Perl code.
913              
914             The runner module is in charge of whatever heavy lifting is required in order
915             to turn your API into a "service", or an "app", or whatever it is you think your
916             API needs to be.
917              
918             The following runners are currently available:
919              
920             =over
921              
922             =item * L - Directly use an API from Perl code.
923              
924             =item * L - Turn an API into a Plack based, JSON-to-JSON
925             RESTful web application.
926              
927             =item * L - Turn an API into a JSON-to-JSON
928             Gearman worker.
929              
930             =item * L - Turn an API into a WebSocket server.
931              
932             =item * L - Turn an API into a JSON-to-JSON ZeroMQ REP worker.
933              
934             =back
935              
936             The latter four completely change the way your API is used, and yet you can
937             see their code is very short.
938              
939             To tell C which runner module to use, you must provide the name of the
940             runner when loading your API:
941              
942             use MyAPI -withPSGI; # can also write -WithPSGI
943              
944             In the above example, C will be the runner module used.
945              
946             The default runner module is C. If you C an API with no
947             parameter, it will be the loaded runner module:
948              
949             use MyAPI;
950              
951             use MyAPI -directly; # the same as above
952              
953             You can easily create your own runner modules, so that your APIs can be used
954             in different ways. A runner module needs to implement the following interface:
955              
956             =head2 init( $runner_class, $target_class )
957              
958             This method is called when C is first imported into an API topic.
959             C<$target_class> will hold the name of the class currently being imported to.
960              
961             You can do whatever initializations you need to do here, possibly manipulating
962             the target class directly. You will probably only want to do this on the root
963             topic, which is why L is available on C<$target_class>.
964              
965             You can look at C and C to see how they're using the
966             C method. For example, in C, L is added
967             to the C<@ISA> array of the root topic, so that it turns into a Plack app. In
968             C, the C method is used to define a C method
969             on the root topic, so that your API can run as any standard Gearman worker.
970              
971             =head2 generate_env( $runner_class, @call_args )
972              
973             This method receives whatever arguments were passed to the L
974             method. It is in charge of returning a standard hash-ref that C can use
975             in order to determine which route the caller wants to execute, and with what
976             parameters. Remember that the way C is invoked depends on the runner used.
977              
978             The hash-ref returned I have the following key-value pairs:
979              
980             =over
981              
982             =item * ROUTE - The route to execute (string).
983              
984             =item * METHOD - The method to call on the route (string).
985              
986             =item * PAYLOAD - A hash-ref of parameters to provide for the method. If no parameters
987             are provided, an empty hash-ref should be given.
988              
989             =back
990              
991             The returned hash-ref is called C<$env>, inspired by L.
992              
993             =head2 generate_res( $runner_class, \%env, $result )
994              
995             This method formats the result from a route before returning it to the caller.
996             It receives the C<$env> hash-ref (if needed), and the result from the route. In the
997             C runner, for example, this method encodes the result into JSON and
998             returns a proper PSGI response array-ref.
999              
1000             =head2 handle_exception( $runner_class, $error, @args )
1001              
1002             This method will be called whenever a route raises an exception, or otherwise your code
1003             fails. The C<$error> variable will always be a standard L,
1004             with C and C keys, and possibly more. Read the discussion above.
1005              
1006             The method should format the error before returning it to the user, similar to what
1007             C above performs, but it allows you to handle exceptions gracefully.
1008              
1009             Whatever arguments were provided to C will be provided to this method as-is,
1010             so that you can inspect or use them if need be. C, for example,
1011             will get the L object and call the C method on it,
1012             to properly indicate the job failed.
1013              
1014             =head1 CONFIGURATION AND ENVIRONMENT
1015            
1016             No configuration files or environment variables required.
1017            
1018             =head1 DEPENDENCIES
1019            
1020             C depends on the following CPAN modules:
1021            
1022             =over
1023            
1024             =item * L
1025            
1026             =item * L
1027              
1028             =item * L
1029              
1030             =item * L
1031            
1032             =item * L
1033            
1034             =back
1035            
1036             The command line utility, L, depends on the following CPAN modules:
1037            
1038             =over
1039              
1040             =item * L
1041              
1042             =item * L
1043              
1044             =item * L
1045              
1046             =back
1047              
1048             =head1 INCOMPATIBILITIES WITH OTHER MODULES
1049              
1050             None reported.
1051              
1052             =head1 BUGS AND LIMITATIONS
1053              
1054             Please report any bugs or feature requests to
1055             C, or through the web interface at
1056             L.
1057              
1058             =head1 SUPPORT
1059              
1060             You can find documentation for this module with the perldoc command.
1061              
1062             perldoc McBain
1063              
1064             You can also look for information at:
1065              
1066             =over 4
1067            
1068             =item * RT: CPAN's request tracker
1069            
1070             L
1071            
1072             =item * AnnoCPAN: Annotated CPAN documentation
1073            
1074             L
1075            
1076             =item * CPAN Ratings
1077            
1078             L
1079            
1080             =item * Search CPAN
1081            
1082             L
1083            
1084             =back
1085            
1086             =head1 AUTHOR
1087            
1088             Ido Perlmuter
1089            
1090             =head1 LICENSE AND COPYRIGHT
1091            
1092             Copyright (c) 2013-2014, Ido Perlmuter C<< ido@ido50.net >>.
1093            
1094             This module is free software; you can redistribute it and/or
1095             modify it under the same terms as Perl itself, either version
1096             5.8.1 or any later version. See L
1097             and L.
1098            
1099             The full text of the license can be found in the
1100             LICENSE file included with this module.
1101            
1102             =head1 DISCLAIMER OF WARRANTY
1103            
1104             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
1105             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
1106             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
1107             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
1108             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1109             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
1110             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
1111             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
1112             NECESSARY SERVICING, REPAIR, OR CORRECTION.
1113            
1114             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1115             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
1116             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
1117             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
1118             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
1119             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
1120             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
1121             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
1122             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
1123             SUCH DAMAGES.
1124            
1125             =cut
1126              
1127             1;
1128             __END__