File Coverage

blib/lib/Kelp/Less.pm
Criterion Covered Total %
statement 40 44 90.9
branch 5 14 35.7
condition n/a
subroutine 20 24 83.3
pod 17 20 85.0
total 82 102 80.3


line stmt bran cond sub pod time code
1             package Kelp::Less;
2              
3 2     2   3335 use Kelp;
  2         8  
  2         13  
4 2     2   14 use Kelp::Base -strict;
  2         3  
  2         7  
5              
6             our @EXPORT = qw/
7             app
8             attr
9             config
10             del
11             debug
12             error
13             get
14             module
15             named
16             param
17             post
18             put
19             req
20             res
21             route
22             run
23             session
24             stash
25             template
26             view
27             /;
28              
29             our $app;
30              
31             sub import
32             {
33 2     2   22 my $class = shift;
34 2         5 my $caller = caller;
35 2     2   13 no strict 'refs';
  2         13  
  2         1864  
36 2         6 for my $sub (@EXPORT) {
37 40         2166 *{"${caller}::$sub"} = eval("\\\&$sub");
  40         334  
38             }
39              
40 2         14 strict->import;
41 2         49 warnings->import;
42 2         164 feature->import(':5.10');
43              
44 2         11 $app = Kelp->new(config_module => 'Config::Less', @_);
45 2         8 $app->routes->base('main');
46             }
47              
48             sub route
49             {
50 17     17 1 59 my ($path, $to) = @_;
51 17         102 $app->add_route($path, $to);
52             }
53              
54             sub get
55             {
56 2     2 1 18 my ($path, $to) = @_;
57 2 50       18 route ref($path) ? $path : [GET => $path], $to;
58             }
59              
60             sub post
61             {
62 1     1 1 5 my ($path, $to) = @_;
63 1 50       9 route ref($path) ? $path : [POST => $path], $to;
64             }
65              
66             sub put
67             {
68 1     1 1 4 my ($path, $to) = @_;
69 1 50       8 route ref($path) ? $path : [PUT => $path], $to;
70             }
71              
72             sub del
73             {
74 1     1 1 4 my ($path, $to) = @_;
75 1 50       7 route ref($path) ? $path : [DELETE => $path], $to;
76             }
77              
78             sub run
79             {
80              
81             # If we're running a test, then return the entire app,
82             # otherwise return the PSGI subroutine
83 1 50   1 1 59 return $ENV{KELP_TESTING} ? $app : $app->run;
84             }
85              
86 3     3 1 30 sub app { $app }
87 2     2 1 9 sub attr { Kelp::Base::attr(ref($app), @_) }
88 2     2 1 20 sub param { $app->param(@_) }
89 0     0 0 0 sub session { $app->session(@_) }
90 2     2 1 15 sub stash { $app->stash(@_) }
91 1     1 1 10 sub named { $app->named(@_) }
92 1     1 1 8 sub req { $app->req }
93 1     1 1 8 sub res { $app->res }
94 1     1 1 12 sub template { $app->res->template(@_) }
95 0     0 1 0 sub view { $app->res->template(@_) }
96 0 0   0 0 0 sub debug { $app->debug(@_) if $app->can('debug') }
97 0 0   0 0 0 sub error { $app->error(@_) if $app->can('error') }
98 2     2 1 257688 sub module { $app->load_module(@_) }
99 1     1 1 10 sub config { $app->config(@_) }
100              
101             1;
102              
103             __END__
104              
105             =pod
106              
107             =head1 NAME
108              
109             Kelp::Less - Quick prototyping with Kelp
110              
111             =head1 SYNOPSIS
112              
113             use Kelp::Less;
114              
115             get '/person/:name' => sub {
116             "Hello " . named 'name';
117             };
118              
119             run;
120              
121             =head1 DESCRIPTION
122              
123             This class exists to provide a way for quick and sloppy prototyping of a web
124             application. It is a wrapper for L<Kelp>, which imports several keywords, making
125             it easier and less verbose to create a quick web app.
126              
127             It's called C<Less>, because there is less typing involved, and
128             because it is suited for smaller, less complicated web projects. We encourage
129             you to use it anywhere you see fit, however for mid-size and big applications we
130             recommend that you use the fully structured L<Kelp>. This way you can take
131             advantage of its powerful router, initialization and testing capabilities.
132              
133             =head1 QUICK START
134              
135             Each web app begins with C<use Kelp::Less;>. This automatically imports C<strict>,
136             C<warnings>, C<v5.10> as well as several useful functions. You can pass any
137             parameters to the constructor at the C<use> statement:
138              
139             use Kelp::Less mode => 'development';
140              
141             The above is equivalent to:
142              
143             use Kelp;
144             my $app = Kelp->new( mode => 'development' );
145              
146             After that, you could add any initializations and attributes. For example, connect
147             to a database or setup cache. C<Kelp::Less> exports L<attr|Kelp::Base/attr>,
148             so you can use it to register attributes to your app.
149              
150             # Connect to DBI and CHI right away
151             attr dbh => sub {
152             DBI->connect( @{ app->config('database') } );
153             };
154              
155             attr cache => sub {
156             CHI->new( @{ app->config('cache') } );
157             };
158              
159             # Another lazy attribute.
160             attr version => sub {
161             app->dbh->selectrow_array("SELECT version FROM vars");
162             };
163              
164             # Later:
165             app->dbh->do(...);
166             app->cache->get(...);
167             if ( app->version ) { ... }
168              
169             Now is a good time to add routes. Routes are added via the L</route> keyword and
170             they are automatically registered in your app. A route needs two parameters -
171             C<path> and C<destination>. These are exactly equivalent to L<Kelp::Routes/add>,
172             and you are encouraged to read its POD to get familiar with how to define routes.
173             Here are a few examples for the impatient:
174              
175             # Add a 'catch-all-methods' route and send it to an anonymous sub
176             route '/hello/:name' => sub {
177             return "Hello " . named('name');
178             };
179              
180             # Add a POST route
181             route [ POST => '/edit/:id' ] => sub {
182             # Do something with named('id')
183             };
184              
185             # Route that runs an existing sub in your code
186             route '/login' => 'login';
187             sub login {
188             ...
189             }
190              
191             Each route subroutine receives C<$self> and all named placeholders.
192              
193             route '/:id/:page' => sub {
194             my ( $self, $id, $page ) = @_;
195             };
196              
197             Here, C<$self> is the app object and it can be used the same way as in a full
198             L<Kelp> route. For the feeling of magic and eeriness, C<Kelp::Lite> aliases
199             C<app> to C<$self>, so the former can be used as a full substitute to the
200             latter. See the exported keywords section for more information.
201              
202             After you have added all of your routes, it is time to run the app. This is done
203             via a single command:
204              
205             run;
206              
207             It returns PSGI ready subroutine, so you can immediately deploy your new app via
208             Plack:
209              
210             > plackup myapp.psgi
211             HTTP::Server::PSGI: Accepting connections at http://0:5000/
212              
213             =head1 KEYWORDS
214              
215             The following list of keywords are exported to allow for less typing in
216             C<Kelp::Less>:
217              
218             =head2 app
219              
220             This a full alias for C<$self>. It is the application object, and an
221             instance of the C<Kelp> class. You can use it for anything you would use
222             C<$self> inside a route.
223              
224             route '/die' => sub {
225             app->res->code(500);
226             };
227              
228             =head2 attr
229              
230             Assigns lazy or active attributes (using L<Kelp::Base>) to C<app>. Use it to
231             initialize your application.
232              
233             attr mongo => MongoDB::MongoClient->new( ... );
234              
235             =head2 route
236              
237             Adds a route to C<app>. It is an alias to C<$self-E<gt>routes-E<gt>add>, and requires
238             the exact same parameters. See L<Kelp::Routes> for reference.
239              
240             route '/get-it' => sub { "got it" };
241              
242             =head2 get, post, put, del
243              
244             These are shortcuts to C<route> restricted to the corresponding HTTP method.
245              
246             get '/data' => sub { "Only works with GET" };
247             post '/data' => sub { "Only works with POST" };
248             put '/data' => sub { "Only works with PUT" };
249             del '/data' => sub { "Only works with DELETE" };
250              
251             =head2 param
252              
253             An alias for C<$self-E<gt>param> that gets the GET or POST parameters.
254             When used with no arguments, it will return an array with the names of all http
255             parameters. Otherwise, it will return the value of the requested http parameter.
256              
257             get '/names' => sub {
258             my @names = param;
259             # Now @names contains the names of the params
260             };
261              
262             get '/value' => sub {
263             my $id = param 'id';
264             # Now $is contains the value of 'id'
265             };
266              
267             =head2 stash
268              
269             An alias for C<$self-E<gt>stash>. The stash is a concept originally conceived by the
270             developers of L<Catalyst>. It's a hash that you can use to pass data from one
271             route to another.
272              
273             # Create a bridge route that checks if the user is authenticated, and saves
274             # the username in the stash.
275             get '/user' => { bridge => 1, to => sub {
276             return stash->{username} = app->authenticate();
277             }};
278              
279             # This route is run after the above bridge, so we know that we have an
280             # authenticated user and their username in the stash.
281             get '/user/welcome' => sub {
282             return "Hello " . stash 'username';
283             };
284              
285             With no arguments C<stash> returns the entire stash hash. A single argument is
286             interpreted as the key to the stash hash and its value is returned accordingly.
287              
288             =head2 named
289              
290             An alias for C<$self-E<gt>named>. The C<named> hash contains the names and values of
291             the named placeholders from the current route's path. Much like the C<stash>,
292             with no arguments it returns the entire C<named> hash, and with a single
293             argument it returns the value for the corresponding key in the hash.
294              
295             get '/:name/:id' => sub {
296             my $name = named 'name';
297             my $id = name 'id';
298             };
299              
300             In the above example a GET request to C</james/1000> will initialize C<$name>
301             with C<"james"> and C<$id> with C<1000>.
302              
303             =head2 req
304              
305             An alias for C<$self-E<gt>req>, this provides quick access to the
306             L<Kelp::Request> object for the current route.
307              
308             # Inside a route
309             if ( req->is_ajax ) {
310             ...
311             }
312              
313             =head2 res
314              
315             An alias for C<$self-E<gt>res>, this is a shortcut for the L<Kelp::Response>
316             object for the current route.
317              
318             # Inside a route
319             res->code(403);
320             res->json->render({ message => "Forbidden" });
321              
322             =head2 template
323              
324             A shortcut to C<$self-E<gt>res-E<gt>template>. Renders a template using the
325             currently loaded template module. Note that a Kelp::Less application does not
326             by default load a template module, so you will have to load it yourself.
327              
328             use Kelp::Less;
329              
330             module 'Template', path => 'views';
331              
332             get '/hello/:name' => sub {
333             template 'hello.tt', { name => named 'name' };
334             };
335              
336             =head2 view
337              
338             A shortcut for L</template>.
339              
340             get '/hello/:name' => sub {
341             view 'hello.tt', { name => named 'name' };
342             };
343              
344             =head2 run
345              
346             Creates and returns a PSGI ready subroutine, and makes the app ready for C<Plack>.
347              
348             =head2 module
349              
350             Loads a Kelp module. The module options may be specified after the module name.
351              
352             module 'JSON::XS', pretty => 1;
353              
354             =head2 config
355              
356             Provides procedural interface to the configuration.
357              
358             get '/hello' => sub {
359             my $baz = config('bar.foo.baz');
360             };
361              
362             =head1 TESTING
363              
364             When writing a C<Kelp::Less> app, we don't have a separate class to initialize and
365             feed into a L<Kelp::Test> object, because all of our code is contained in the
366             C<app.psgi> file. In this case, the C<Kelp::Test> object can be initialized
367             with the name of the C<PSGI> file in the C<psgi> argument.
368              
369             # t/main.t
370             use Kelp::Test;
371              
372             my $t = Kelp::Test->new( psgi => 'app.psgi' );
373             # Do some tests ...
374              
375             Since you don't have control over the creation of the C<Kelp> object, if you
376             need to specify a different mode for testing, you can use the C<PLACK_ENV>
377             environmental variable:
378              
379             > PLACK_ENV=test prove -l
380              
381             This will enable the C<conf/test.pl> configuration, which you should
382             tailor to your testing needs.
383              
384             =head1 ACKNOWLEDGEMENTS
385              
386             This module's interface was inspired by L<Dancer>, which in its turn was
387             inspired by Sinatra, so Viva La Open Source!
388              
389             =cut