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