blib/lib/KelpX/Sweet.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 162 | 192 | 84.3 |
branch | 31 | 66 | 46.9 |
condition | 1 | 6 | 16.6 |
subroutine | 22 | 25 | 88.0 |
pod | 0 | 1 | 0.0 |
total | 216 | 290 | 74.4 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package KelpX::Sweet; | ||||||
2 | |||||||
3 | 7 | 7 | 26480 | use warnings; | |||
7 | 15 | ||||||
7 | 226 | ||||||
4 | 7 | 7 | 29 | use strict; | |||
7 | 9 | ||||||
7 | 177 | ||||||
5 | 7 | 7 | 3086 | use true; | |||
7 | 61694 | ||||||
7 | 80 | ||||||
6 | 7 | 7 | 10042 | use Text::ASCIITable; | |||
7 | 102219 | ||||||
7 | 354 | ||||||
7 | 7 | 7 | 3417 | use FindBin; | |||
7 | 5899 | ||||||
7 | 310 | ||||||
8 | 7 | 7 | 2958 | use Module::Find 'useall'; | |||
7 | 6805 | ||||||
7 | 391 | ||||||
9 | 7 | 7 | 39 | use base 'Kelp'; | |||
7 | 10 | ||||||
7 | 3692 | ||||||
10 | |||||||
11 | our $VERSION = '0.003'; | ||||||
12 | |||||||
13 | sub import { | ||||||
14 | 7 | 7 | 61 | my ($class, %opts) = @_; | |||
15 | 7 | 82 | strict->import(); | ||||
16 | 7 | 139 | warnings->import(); | ||||
17 | 7 | 43 | true->import(); | ||||
18 | 7 | 5079 | my $caller = caller; | ||||
19 | 7 | 16 | my $routes = []; | ||||
20 | 7 | 8 | my $configs = {}; | ||||
21 | 7 | 20 | my $auto = 0; | ||||
22 | { | ||||||
23 | 7 | 7 | 512828 | no strict 'refs'; | |||
7 | 11 | ||||||
7 | 7544 | ||||||
7 | 8 | ||||||
24 | 7 | 10 | push @{"${caller}::ISA"}, 'Kelp'; | ||||
7 | 79 | ||||||
25 | # check args | ||||||
26 | # auto load routes? | ||||||
27 | 7 | 50 | 28 | if ($opts{"-auto"}) { | |||
28 | 0 | 0 | $auto = 1; | ||||
29 | 0 | 0 | my $route_tb = Text::ASCIITable->new; | ||||
30 | 0 | 0 | $route_tb->setCols('Routes'); | ||||
31 | 0 | 0 | my @mod_routes = useall "${caller}::Route"; | ||||
32 | 0 | 0 | for my $mod (@mod_routes) { | ||||
33 | 0 | 0 | $route_tb->addRow($mod); | ||||
34 | 0 | 0 | push @$routes, $mod->get_routes(); | ||||
35 | } | ||||||
36 | |||||||
37 | 0 | 0 | print $route_tb . "\n"; | ||||
38 | } | ||||||
39 | |||||||
40 | 7 | 6 | 23 | *{"${caller}::new"} = sub { return shift->SUPER::new(@_); }; | |||
7 | 24 | ||||||
6 | 116 | ||||||
41 | 7 | 21 | *{"${caller}::maps"} = sub { | ||||
42 | 6 | 50 | 6 | 30 | die "Please don't use -auto and maps at the same time\n" | ||
43 | if $auto; | ||||||
44 | |||||||
45 | 6 | 7 | my ($route_names) = @_; | ||||
46 | 6 | 50 | 24 | unless (ref $route_names eq 'ARRAY') { | |||
47 | 0 | 0 | die "routes() expects an array references"; | ||||
48 | } | ||||||
49 | |||||||
50 | 6 | 44 | my $route_tb = Text::ASCIITable->new; | ||||
51 | 6 | 226 | $route_tb->setCols('Routes'); | ||||
52 | 6 | 584 | for my $mod (@$route_names) { | ||||
53 | 6 | 17 | my $route_path = "${caller}::Route::${mod}"; | ||||
54 | 6 | 6 | 439 | eval "use $route_path;"; | |||
6 | 2109 | ||||||
6 | 12 | ||||||
6 | 67 | ||||||
55 | 6 | 50 | 123 | if ($@) { | |||
56 | 0 | 0 | warn "Could not load route ${route_path}: $@"; | ||||
57 | 0 | 0 | next; | ||||
58 | } | ||||||
59 | |||||||
60 | 6 | 34 | $route_tb->addRow($route_path); | ||||
61 | 6 | 1019 | push @$routes, $route_path->get_routes(); | ||||
62 | } | ||||||
63 | |||||||
64 | 6 | 30 | print $route_tb . "\n"; | ||||
65 | 7 | 28 | }; | ||||
66 | |||||||
67 | 7 | 17 | *{"${caller}::model"} = sub { | ||||
68 | 1 | 1 | 21147 | my ($self, $model) = @_; | |||
69 | 1 | 18 | return $self->{_models}->{$model}; | ||||
70 | 7 | 18 | }; | ||||
71 | |||||||
72 | 7 | 0 | 16 | *{"${caller}::path_to"} = sub { return $FindBin::Bin; }; | |||
7 | 21 | ||||||
0 | 0 | ||||||
73 | |||||||
74 | 7 | 19 | *{"${caller}::cfg"} = sub { | ||||
75 | 0 | 0 | 0 | my ($key, $hash) = @_; | |||
76 | 0 | 0 | $configs->{$key} = $hash; | ||||
77 | 7 | 15 | }; | ||||
78 | |||||||
79 | 7 | 33 | *{"${caller}::build"} = sub { | ||||
80 | 6 | 6 | 1124 | my ($self) = @_; | |||
81 | 6 | 27 | my $config = $self->config_hash; | ||||
82 | # config | ||||||
83 | 6 | 50 | 46 | if (scalar keys %$configs > 0) { | |||
84 | 0 | 0 | for my $key (keys %$configs) { | ||||
85 | 0 | 0 | $config->{"+${key}"} = $configs->{$key}; | ||||
86 | } | ||||||
87 | } | ||||||
88 | |||||||
89 | # models | ||||||
90 | 6 | 50 | 24 | if ($config->{models}) { | |||
91 | 6 | 18 | $self->{_models} = {}; | ||||
92 | 6 | 54 | my $model_tb = Text::ASCIITable->new; | ||||
93 | 6 | 276 | $model_tb->setCols('Model', 'Alias'); | ||||
94 | 6 | 50 | 588 | unless (ref $config->{models} eq 'HASH') { | |||
95 | 0 | 0 | die "config: models expects a hash reference\n"; | ||||
96 | } | ||||||
97 | |||||||
98 | 6 | 9 | for my $model (keys %{$config->{models}}) { | ||||
6 | 26 | ||||||
99 | 6 | 10 | my $name = $model; | ||||
100 | 6 | 14 | my $opts = $config->{models}->{$model}; | ||||
101 | 6 | 13 | my $mod = $opts->{model}; | ||||
102 | 6 | 6 | 457 | eval "use $mod;"; | |||
6 | 2066 | ||||||
6 | 823801 | ||||||
6 | 128 | ||||||
103 | 6 | 50 | 30 | if ($@) { | |||
104 | 0 | 0 | die "Could not load model $mod: $@"; | ||||
105 | } | ||||||
106 | |||||||
107 | 6 | 11 | my @args = @{$opts->{args}}; | ||||
6 | 33 | ||||||
108 | 6 | 50 | 24 | if (my $ret = $mod->build(@args)) { | |||
109 | 6 | 50 | 337475 | if (ref $ret) { | |||
110 | 6 | 52 | $model_tb->addRow($mod, $name); | ||||
111 | # returned a standard hash reference | ||||||
112 | 6 | 50 | 940 | if (ref $ret eq 'HASH') { | |||
113 | 0 | 0 | foreach my $key (keys %$ret) { | ||||
114 | 0 | 0 | 0 | if (ref $ret->{$key}) { | |||
115 | 0 | 0 | $self->{_models}->{"${name}::${key}"} = $ret->{$key}; | ||||
116 | 0 | 0 | $model_tb->addRow(ref $ret->{$key}, "${name}::${key}"); | ||||
117 | } | ||||||
118 | } | ||||||
119 | } | ||||||
120 | else { | ||||||
121 | 6 | 25 | $self->{_models}->{$name} = $ret; | ||||
122 | |||||||
123 | # is this dbix::class? | ||||||
124 | 6 | 55 | require mro; | ||||
125 | 6 | 16 | my $dbref = ref $ret; | ||||
126 | 6 | 50 | 10 | if (grep { $_ eq 'DBIx::Class::Schema' } @{mro::get_linear_isa($dbref)}) { | |||
42 | 62 | ||||||
6 | 34 | ||||||
127 | 6 | 50 | 398 | if ($dbref->can('sources')) { | |||
128 | 6 | 29 | my @sources = $dbref->sources; | ||||
129 | 6 | 319 | for my $source (@sources) { | ||||
130 | 6 | 58 | $self->{_models}->{"${name}::${source}"} = $ret->resultset($source); | ||||
131 | 6 | 5432 | $model_tb->addRow("${dbref}::ResultSet::${source}", "${name}::${source}"); | ||||
132 | } | ||||||
133 | } | ||||||
134 | } | ||||||
135 | } | ||||||
136 | } | ||||||
137 | else { | ||||||
138 | 0 | 0 | die "Did not return a valid object from models build(): $name\n"; | ||||
139 | } | ||||||
140 | } | ||||||
141 | else { | ||||||
142 | 0 | 0 | die "build() failed: $mod"; | ||||
143 | } | ||||||
144 | } | ||||||
145 | |||||||
146 | 6 | 50 | 643 | if (scalar keys %{$self->{_models}} > 0) { | |||
6 | 43 | ||||||
147 | 6 | 30 | print $model_tb . "\n"; | ||||
148 | } | ||||||
149 | } | ||||||
150 | # routes | ||||||
151 | 6 | 5379 | my $r = $self->routes; | ||||
152 | 6 | 39 | for my $route (@$routes) { | ||||
153 | 6 | 27 | for my $url (keys %$route) { | ||||
154 | 36 | 100 | 17309 | if ($route->{$url}->{bridge}) { | |||
50 | |||||||
155 | 6 | 50 | $r->add([ uc($route->{$url}->{type}) => $url ], { to => $route->{$url}->{coderef}, bridge => 1 }); | ||||
156 | } | ||||||
157 | elsif ($route->{$url}->{type} eq 'any') { | ||||||
158 | 0 | 0 | $r->add($url, $route->{$url}->{coderef}); | ||||
159 | } | ||||||
160 | else { | ||||||
161 | 30 | 160 | $r->add([ uc($route->{$url}->{type}) => $url ], $route->{$url}->{coderef}); | ||||
162 | } | ||||||
163 | } | ||||||
164 | } | ||||||
165 | 7 | 42 | }; | ||||
166 | |||||||
167 | 7 | 24 | *{"${caller}::detach"} = sub { | ||||
168 | 1 | 1 | 23984 | my ($self) = @_; | |||
169 | |||||||
170 | 1 | 12 | my @caller = caller(1); | ||||
171 | 1 | 2 | my $fullpath = $caller[3]; | ||||
172 | 1 | 2 | my $name; | ||||
173 | 1 | 50 | 9 | if ($fullpath =~ /.+::(.+)$/) { | |||
174 | 1 | 2 | $name = $1; | ||||
175 | } | ||||||
176 | |||||||
177 | 1 | 50 | 3 | if ($name) { | |||
178 | 1 | 50 | 4 | print "[debug] Rendering template: $name\n" if $ENV{KELPX_SWEET_DEBUG}; | |||
179 | 1 | 3 | $self->template($name, $self->stash); | ||||
180 | } | ||||||
181 | 7 | 18 | }; | ||||
182 | |||||||
183 | # if 'has' is not available (ie: no Moose, Moo, Mouse, etc), then import our own small version | ||||||
184 | 7 | 50 | 80 | unless ($caller->can('has')) { | |||
185 | 7 | 11 | *{"${caller}::has"} = \&_has; | ||||
7 | 16 | ||||||
186 | } | ||||||
187 | |||||||
188 | # if 'around' is not available, import a small version of our own | ||||||
189 | { | ||||||
190 | 7 | 7 | 38 | no warnings 'redefine'; | |||
7 | 10 | ||||||
7 | 1590 | ||||||
7 | 10 | ||||||
191 | 7 | 50 | 49 | unless ($caller->can('around')) { | |||
192 | 7 | 733 | *{"${caller}::around"} = sub { | ||||
193 | 6 | 6 | 3333 | my ($method, $code) = @_; | |||
194 | |||||||
195 | 6 | 15 | my $fullpkg = "${caller}::${method}"; | ||||
196 | 6 | 16 | my $old_code = \&$fullpkg; | ||||
197 | 6 | 101 | *{"${fullpkg}"} = sub { | ||||
198 | 6 | 6 | 409892 | $code->($old_code, @_); | |||
199 | 6 | 18 | }; | ||||
200 | 7 | 20 | }; | ||||
201 | } | ||||||
202 | } | ||||||
203 | } | ||||||
204 | } | ||||||
205 | |||||||
206 | sub _has { | ||||||
207 | 6 | 6 | 48 | my ($acc, %attrs) = @_; | |||
208 | 6 | 12 | my $class = caller; | ||||
209 | 6 | 6 | my $ro = 0; | ||||
210 | 6 | 6 | my $rq = 0; | ||||
211 | 6 | 6 | my $df; | ||||
212 | 6 | 50 | 18 | if (%attrs) { | |||
213 | 6 | 50 | 18 | if ($attrs{is} eq 'ro') { $ro = 1; } | |||
6 | 9 | ||||||
214 | 6 | 50 | 14 | if ($attrs{required}) { $rq = 1; } | |||
6 | 9 | ||||||
215 | 6 | 50 | 15 | if ($attrs{default}) { $df = $attrs{default}; } | |||
6 | 8 | ||||||
216 | |||||||
217 | 6 | 50 | 19 | if ($df) { | |||
218 | 6 | 50 | 19 | die "has: default expects a code reference\n" | |||
219 | unless ref $df eq 'CODE'; | ||||||
220 | } | ||||||
221 | } | ||||||
222 | |||||||
223 | { | ||||||
224 | 7 | 7 | 90 | no strict 'refs'; | |||
7 | 9 | ||||||
7 | 1778 | ||||||
6 | 6 | ||||||
225 | 6 | 37 | *{"${class}::${acc}"} = sub { | ||||
226 | #if ($attrs{default}) { $_[0]->{$name} = $attrs{default}; } | ||||||
227 | 1 | 50 | 33 | 1 | 26093 | if ($rq and not $df) { | |
228 | 0 | 0 | 0 | 0 | if (not $_[0]->{$acc} and not $_[1]) { | ||
229 | 0 | 0 | die "You attempted to use a field that can't be left blank: ${acc}"; | ||||
230 | } | ||||||
231 | } | ||||||
232 | |||||||
233 | 1 | 50 | 4 | if ($df) { $_[0]->{$acc} = $df->(); } | |||
1 | 5 | ||||||
234 | |||||||
235 | 1 | 50 | 6 | if (@_ == 2) { | |||
236 | 0 | 0 | 0 | die "Can't modify a readonly accessor: ${acc}" | |||
237 | if $ro; | ||||||
238 | 0 | 0 | $_[0]->{$acc} = $_[1]; | ||||
239 | } | ||||||
240 | 1 | 4 | return $_[0]->{$acc}; | ||||
241 | 6 | 18 | }; | ||||
242 | } | ||||||
243 | } | ||||||
244 | |||||||
245 | sub new { | ||||||
246 | 0 | 0 | 0 | 0 | bless { @_[ 1 .. $#_ ] }, $_[0]; | ||
247 | } | ||||||
248 | |||||||
249 | =head1 NAME | ||||||
250 | |||||||
251 | KelpX::Sweet - Kelp with extra sweeteners | ||||||
252 | |||||||
253 | =head1 DESCRIPTION | ||||||
254 | |||||||
255 | Kelp is good. Kelp is great. But what if you could give it more syntactic sugar and separate your routes from the logic in a cleaner way? KelpX::Sweet attempts to do just that. | ||||||
256 | |||||||
257 | =head1 SIMPLE TUTORIAL | ||||||
258 | |||||||
259 | For the most part, your original C |
||||||
260 | |||||||
261 | B |
||||||
262 | |||||||
263 | package MyApp; | ||||||
264 | use KelpX::Sweet; | ||||||
265 | |||||||
266 | maps ['Main']; | ||||||
267 | |||||||
268 | Yep, that's the complete code for your base. You pass C |
||||||
269 | It will look for them in C |
||||||
270 | Next, let's create that file | ||||||
271 | |||||||
272 | B |
||||||
273 | |||||||
274 | package MyApp::Route::Main; | ||||||
275 | |||||||
276 | use KelpX::Sweet::Route; | ||||||
277 | |||||||
278 | get '/' => 'Controller::Root::hello'; | ||||||
279 | get '/nocontroller' => sub { 'Hello, world from no controller!' }; | ||||||
280 | |||||||
281 | Simply use C |
||||||
282 | but that makes the whole idea of this module pointless ;) | ||||||
283 | It will load C |
||||||
284 | any of your arguments will also be sent the method inside that controller, so you don't need to do anything else! | ||||||
285 | |||||||
286 | Finally, we can create the controller | ||||||
287 | |||||||
288 | B |
||||||
289 | |||||||
290 | package MyApp::Controller::Root; | ||||||
291 | |||||||
292 | use KelpX::Sweet::Controller; | ||||||
293 | |||||||
294 | sub hello { | ||||||
295 | my ($self) = @_; | ||||||
296 | return "Hello, world!"; | ||||||
297 | } | ||||||
298 | |||||||
299 | You now have a fully functional Kelp app! Remember, because this module is just a wrapper, you can do pretty much anything L |
||||||
300 | can, like C<$self->>param> for example. | ||||||
301 | |||||||
302 | =head1 SUGARY SYNTAX | ||||||
303 | |||||||
304 | By sugar, we mean human readable and easy to use. You no longer need a build method, then to call ->add on an object for your | ||||||
305 | routes. It uses a similar syntax to L |
||||||
306 | |||||||
307 | =head2 get | ||||||
308 | |||||||
309 | This will trigger a standard GET request. | ||||||
310 | |||||||
311 | get '/mypage' => sub { 'It works' }; | ||||||
312 | |||||||
313 | =head2 post | ||||||
314 | |||||||
315 | Will trigger on POST requests only | ||||||
316 | |||||||
317 | post '/someform' => sub { 'Posted like a boss' }; | ||||||
318 | |||||||
319 | =head2 any | ||||||
320 | |||||||
321 | Will trigger on POST B |
||||||
322 | |||||||
323 | any '/omni' => sub { 'Hit me up on any request' }; | ||||||
324 | |||||||
325 | =head2 bridge | ||||||
326 | |||||||
327 | Bridges are cool, so please check out the Kelp documentation for more information on what they do and how they work. | ||||||
328 | |||||||
329 | bridge '/users/:id' => sub { | ||||||
330 | unless ($self->user->logged_in) { | ||||||
331 | return; | ||||||
332 | } | ||||||
333 | |||||||
334 | return 1; | ||||||
335 | }; | ||||||
336 | |||||||
337 | get '/users/:id/view' => 'Controller::Users::view'; | ||||||
338 | |||||||
339 | =head2 has | ||||||
340 | |||||||
341 | If you only want basic accessors and KelpX::Sweet detects you don't have any OOP frameworks activated with C |
||||||
342 | own little method which works similar to L |
||||||
343 | |||||||
344 | package MyApp; | ||||||
345 | |||||||
346 | use KelpX::Sweet; | ||||||
347 | has 'x' => ( is => 'rw', default => sub { "Hello, world" } ); | ||||||
348 | |||||||
349 | package MyApp::Controller::Main; | ||||||
350 | |||||||
351 | use KelpX::Sweet::Controller; | ||||||
352 | |||||||
353 | sub hello { shift->x; } # Hello, world | ||||||
354 | |||||||
355 | =head2 around | ||||||
356 | |||||||
357 | Need more power? Want to modify the default C |
||||||
358 | This allows you to tap into build if you really want to for some reason. | ||||||
359 | |||||||
360 | package MyApp; | ||||||
361 | |||||||
362 | use KelpX::Sweet; | ||||||
363 | |||||||
364 | around 'build' => sub { | ||||||
365 | my $method = shift; | ||||||
366 | my $self = shift; | ||||||
367 | my $routes = $self->routes; | ||||||
368 | $routes->add('/manual' => sub { "Manually added" }); | ||||||
369 | |||||||
370 | $self->$method(@_); | ||||||
371 | }; | ||||||
372 | |||||||
373 | =head1 MODELS | ||||||
374 | |||||||
375 | You can always use an attribute to create a database connection, or separate them using models in a slightly cleaner way. | ||||||
376 | In your config you supply a hash reference with the models alias (what you will reference it as in code), the full path, and finally any | ||||||
377 | arguments it might have (like the dbi line, username and password). | ||||||
378 | |||||||
379 | # config.pl | ||||||
380 | models => { | ||||||
381 | 'LittleDB' => { | ||||||
382 | 'model' => 'TestApp::Model::LittleDB', | ||||||
383 | 'args' => ['dbi:SQLite:testapp.db'], | ||||||
384 | }, | ||||||
385 | }, | ||||||
386 | |||||||
387 | Then, you create C |
||||||
388 | |||||||
389 | package TestApp::Model::LittleDB; | ||||||
390 | |||||||
391 | use KelpX::Sweet::Model; | ||||||
392 | use DBIx::Lite; | ||||||
393 | |||||||
394 | sub build { | ||||||
395 | my ($self, @args) = @_; | ||||||
396 | return DBIx::Lite->connect(@args); | ||||||
397 | } | ||||||
398 | |||||||
399 | As you can see, the C |
||||||
400 | |||||||
401 | That's all you need. Now you can pull that model instance out at any time in your controllers with C |
||||||
402 | |||||||
403 | package TestApp::Controller::User; | ||||||
404 | |||||||
405 | use KelpX::Sweet::Controller; | ||||||
406 | |||||||
407 | sub users { | ||||||
408 | my ($self) = @_; | ||||||
409 | my @users = $self->model('LittleDB')->table('users')->all; | ||||||
410 | return join ', ', map { $_->name } @users; | ||||||
411 | } | ||||||
412 | |||||||
413 | =head2 Named ResultSets | ||||||
414 | |||||||
415 | If you're not using DBIx::Class, you can still have similar styled resultsets. Simply return a standard hash reference instead of a blessed object | ||||||
416 | from the C |
||||||
417 | |||||||
418 | package TestApp::Model::LittleDB; | ||||||
419 | |||||||
420 | use KelpX::Sweet::Model; | ||||||
421 | use DBIx::Lite; | ||||||
422 | |||||||
423 | sub build { | ||||||
424 | my ($self, @args) = @_; | ||||||
425 | my $schema = DBIx::Lite->connect(@args); | ||||||
426 | return { | ||||||
427 | 'User' => $schema->table('users'), | ||||||
428 | 'Product' => $schema->table('products'), | ||||||
429 | }; | ||||||
430 | } | ||||||
431 | |||||||
432 | Then, you can do this stuff in your controllers | ||||||
433 | |||||||
434 | package TestApp::Controller::Assets; | ||||||
435 | |||||||
436 | sub users { | ||||||
437 | my ($self) = @_; | ||||||
438 | my @users = $self->model('LittleDB::User')->all; | ||||||
439 | return join " ", map { $_->name . " (" . $_->email . ")" } @users; |
||||||
440 | } | ||||||
441 | |||||||
442 | sub products { | ||||||
443 | my ($self) = @_; | ||||||
444 | my @products = $self->model('LittleDB::Product')->all; | ||||||
445 | return join " ", map { $_->name . " (" . sprintf("%.2f", $_->value) . ")" } @products; |
||||||
446 | } | ||||||
447 | |||||||
448 | =head2 Models and DBIx::Class | ||||||
449 | |||||||
450 | If you enjoy the way Catalyst handles DBIx::Class models, you're going to love this (I hope so, at least). KelpX::Sweet will automagically | ||||||
451 | create models based on the sources of your schema if it detects it's a DBIx::Class::Schema. | ||||||
452 | Nothing really has to change, KelpX::Sweet will figure it out on its own. | ||||||
453 | |||||||
454 | package TestApp::Model::LittleDB; | ||||||
455 | |||||||
456 | use KelpX::Sweet::Model; | ||||||
457 | use LittleDB::Schema; | ||||||
458 | |||||||
459 | sub build { | ||||||
460 | my ($self, @args) = @_; | ||||||
461 | return LittleDB::Schema->connect(@args); | ||||||
462 | } | ||||||
463 | |||||||
464 | Then just use it as you normally would in Catalyst (except we store it in C<$self>, not C<$c>). | ||||||
465 | |||||||
466 | package TestApp::Controller::User; | ||||||
467 | |||||||
468 | use KelpX::Sweet::Controller; | ||||||
469 | |||||||
470 | sub users { | ||||||
471 | my ($self) = @_; | ||||||
472 | my @users = $self->model('LittleDB::User')->all; | ||||||
473 | return join ', ', map { $_->name } @users; | ||||||
474 | } | ||||||
475 | |||||||
476 | KelpX::Sweet will loop through all your schemas sources and create models based on your alias, and the sources name. So, C |
||||||
477 | |||||||
478 | When we start our app, even though we've only added LittleDB, you'll see we have the new ones based on our Schema. Neat! | ||||||
479 | |||||||
480 | .----------------------------------------------------------. | ||||||
481 | | Model | Alias | | ||||||
482 | +--------------------------------------+-------------------+ | ||||||
483 | | TestApp::Model::LittleDB | LittleDB | | ||||||
484 | | LittleDB::Schema::ResultSet::User | LittleDB::User | | ||||||
485 | | LittleDB::Schema::ResultSet::Product | LittleDB::Product | | ||||||
486 | '--------------------------------------+-------------------' | ||||||
487 | |||||||
488 | =head1 VIEWS | ||||||
489 | |||||||
490 | OK, so to try and not separate too much, I've chosen not to include views. Just use the standard Kelp modules | ||||||
491 | (ie: L |
||||||
492 | |||||||
493 | =head2 detach | ||||||
494 | |||||||
495 | This method will call C for you with the added benefit of automatically filling out the filename and including whatever | ||||||
496 | is in the stash for you. | ||||||
497 | |||||||
498 | package MyApp::Controller::Awesome; | ||||||
499 | |||||||
500 | use KelpX::Sweet::Controller; | ||||||
501 | |||||||
502 | sub hello { | ||||||
503 | my ($self) = @_; | ||||||
504 | $self->stash->{name} = 'World'; | ||||||
505 | $self->detach; | ||||||
506 | } | ||||||
507 | |||||||
508 | Then, you just create C |
||||||
509 | |||||||
510 | Hello, [% name %] |
||||||
511 | |||||||
512 | While not really required, it does save a bit of typing and can come in quite useful. | ||||||
513 | |||||||
514 | =head1 IMPORT OPTIONS | ||||||
515 | |||||||
516 | =head2 -auto | ||||||
517 | |||||||
518 | Importing -auto will automatically include any route modules within your C |
||||||
519 | For example, we have two controllers, C |
||||||
520 | |||||||
521 | package MyApp::Route::Main; | ||||||
522 | |||||||
523 | use KelpX::Sweet::Route; | ||||||
524 | |||||||
525 | get '/' => sub { "Hi" }; | ||||||
526 | |||||||
527 | package MyApp::Route::New; | ||||||
528 | |||||||
529 | use KelpX::Sweet::Route; | ||||||
530 | |||||||
531 | get '/new/url' => sub { "New one" }; | ||||||
532 | |||||||
533 | Then to kick off our app, all we need is | ||||||
534 | |||||||
535 | package MyApp; | ||||||
536 | use KelpX::Sweet -auto => 1; | ||||||
537 | |||||||
538 | That's it. KelpX::Sweet will complain if you attempt to use C |
||||||
539 | |||||||
540 | =head1 REALLY COOL THINGS TO NOTE | ||||||
541 | |||||||
542 | =head2 Default imports | ||||||
543 | |||||||
544 | You should be aware that KelpX::Sweet will import warnings, strict and true for you. Because of this, there is no requirement to | ||||||
545 | add a true value to the end of your file. I chose this because it just makes things look a little cleaner. | ||||||
546 | |||||||
547 | =head2 KelpX::Sweet starter | ||||||
548 | |||||||
549 | On installation of KelpX::Sweet, you'll receive a file called C |
||||||
550 | and it will create a working test app with minimal boilerplate so you can get started straight away. Just run it as: | ||||||
551 | |||||||
552 | $ kelpx-sweet MyApp | ||||||
553 | $ kelpx-sweet Something::With::A::Larger::Namespace | ||||||
554 | |||||||
555 | =head1 AUTHOR | ||||||
556 | |||||||
557 | Brad Haywood |
||||||
558 | |||||||
559 | =head1 LICENSE | ||||||
560 | |||||||
561 | You may distribute this code under the same terms as Perl itself. | ||||||
562 | |||||||
563 | =cut | ||||||
564 | |||||||
565 | 1; | ||||||
566 | __END__ |