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