Commit | Line | Data |
63bb30b4 |
1 | =head1 NAME |
2 | |
3 | Reaction::Manual::Tutorial.pod - Step by Step Tutorial |
4 | |
5 | =head1 DESCRIPTION |
6 | |
7 | This document aims at giving simple step-by-step leading to an example application |
8 | using the common functionality provided by L<Reaction>. |
9 | |
10 | =head1 CREATING A NEW APPLICATION |
11 | |
12 | At first we have to create a new application. For this we use the C<catalyst.pl> |
13 | script as we would for any other Catalyst application: |
14 | |
15 | $ catalyst.pl MyApp |
16 | [lots "created ..." messages] |
17 | |
18 | There is nothing to change in the application class file. |
19 | |
139b74f7 |
20 | As you work through this tutorial you'll be creating several new files in |
21 | various directories. You can save some time by creating the directories now, |
22 | like this: |
23 | |
24 | mkdir -p share/skin/myapp/layout lib/MyApp/View/Site/Widget lib/MyApp/Schema lib/MyApp/InterfaceModel |
25 | |
63bb30b4 |
26 | =head1 THE VIEW |
27 | |
28 | Since we are not just rendering templates with Reaction, but layouts and widgets, |
29 | a simple TT view won't suffice. We need to create our own C<lib/MyApp/View/Site.pm>: |
30 | |
31 | package MyApp::View::Site; |
32 | use Reaction::Class; |
33 | |
91c2674b |
34 | use namespace::autoclean; |
63bb30b4 |
35 | |
36 | extends 'Reaction::UI::View::TT'; |
37 | |
38 | __PACKAGE__->meta->make_immutable; |
39 | |
40 | 1; |
41 | |
42 | The C<use Reaction::Class> line will import L<Moose>, L<strict> and L<warnings> into |
43 | our file and might perform some Reaction specific setups. |
44 | |
45 | We make sure that we don't provide imported functions as methods at runtime by using |
91c2674b |
46 | L<namespace::autoclean>. |
63bb30b4 |
47 | |
48 | In its simplest version, our view just needs to do a C<extends 'Reaction::UI::View::TT'> |
49 | to make a new subclass of it. |
50 | |
51 | We chose to call C<make_immutable> on the class' meta class instance to have it inline |
52 | methods for runtime speed improvements. |
53 | |
54 | =head1 THE ROOT CONTROLLER |
55 | |
56 | As usual in Catalyst, our root controller (at C<lib/MyApp/Controller/Root.pm> represents |
57 | the root namespace for our application. For this purpose, it should look like this: |
58 | |
59 | package MyApp::Controller::Root; |
60 | use strict; |
61 | use warnings; |
62 | use parent 'Reaction::UI::Controller::Root'; |
63 | |
64 | use aliased 'Reaction::UI::ViewPort'; |
65 | use aliased 'Reaction::UI::ViewPort::SiteLayout'; |
66 | |
91c2674b |
67 | use namespace::autoclean; |
63bb30b4 |
68 | |
69 | __PACKAGE__->config( |
70 | view_name => 'Site', |
71 | window_title => 'MyApp Window', |
72 | namespace => '', |
73 | ); |
74 | |
75 | sub base: Chained('/') PathPart('') CaptureArgs(0) { |
76 | my ($self, $ctx) = @_; |
77 | $self->push_viewport(SiteLayout, |
78 | title => 'MyApp Test Title', |
79 | static_base_uri => join('', $ctx->uri_for('/static')), |
158404c5 |
80 | meta_info => { |
81 | http_header => { |
82 | 'Content-Type' => 'text/html;charset=utf-8', |
83 | }, |
84 | }, |
63bb30b4 |
85 | ); |
86 | } |
87 | |
88 | sub root: Chained('base') PathPart('') Args(0) { |
89 | my ($self, $ctx) = @_; |
90 | $self->push_viewport(ViewPort, layout => 'root'); |
91 | } |
92 | |
93 | 1; |
94 | |
91c2674b |
95 | The effects of L<strict>, L<warnings>, L<parent>, L<aliased> and L<namespace::autoclean> |
96 | should be clear by now. Let's take a look at the configuration. |
63bb30b4 |
97 | |
98 | The C<view_name> determines which view to use. We set it to C<Site>, which is our only view |
99 | by now. Be careful to set C<view_name> and not C<view>, which would fail telling you it |
100 | expected an object. |
101 | |
102 | The C<window_title> is the title given to the L<Reaction::UI::Window> instance that will be |
103 | stored in C<$ctx-E<gt>stash-E<gt>{window}> by the C<begin> action provided by |
104 | L<Reaction::UI::Controller::Root>. |
105 | |
106 | The C<namespace> setting anchors the root controller at C</>. |
107 | |
108 | The C<base> action here acts as a general point all other actions can chain off of. It |
109 | pushes the L<Reaction::UI::ViewPort::SiteLayout> viewport onto the |
110 | L<focus stack|Reaction::UI::FocusStack>. As arguments we see a C<title> that will be used |
111 | as page title later. The C<static_base_uri> is used for static links like CSS and JavaScript |
112 | files. Since we didn't specify a layout C<site_layout> will be used. |
113 | |
114 | We also defined a C<root> action serving as application index. It chains off the C<base> |
115 | action. It is only pushing the root viewport L<Reaction::UI::ViewPort> on the focus stack, |
116 | but this time we specified a layout named C<root>. |
117 | |
118 | Reaction will try to find our layout files in C<share/skin/$skin_name/layout/*>, so the next |
119 | thing to do is to create a new skin and the layout files. |
120 | |
121 | =head1 A NEW SKIN |
122 | |
123 | If your version of Catalyst still creates a C<root> instead of a C<share> directory, you |
124 | might want to rename it. This is regarded as a best practice and follows the conventions |
125 | of this tutorial and other Reaction documentation. |
126 | |
127 | First we need to create a directory for our new skin: |
128 | |
129 | $ mkdir -p share/skin/myapp/layout |
130 | |
131 | Next we need to configure our new skin. This is done in the C<share/skin/myapp/skin.conf> |
132 | file. At the moment, all it should contain is |
133 | |
134 | extends /Reaction/default |
135 | |
136 | Note that this C<extends> specification contains the distribution name of the |
137 | library or application of which to use the templates as base. You can also give it |
138 | a relative name like |
139 | |
140 | extends foo |
141 | |
142 | and it would try to extend a skin named C<foo> in your own application's C<share/skin> |
143 | directory. |
144 | |
753f8f1d |
145 | In general, the Reaction distribution provides two skin choices to build upon: |
146 | C</Reaction/default> and C</Reaction/base>. C<base> is Reaction's most basic skin |
147 | while the C<default> skin extends the base by embellishing the layouts with more XHTML. |
148 | The default skin is designed to be a working skin. |
149 | |
150 | In other words, if you want to write most of the low-level XHTML yourself |
151 | then put the following in your skin.conf: |
152 | |
153 | extends /Reaction/base |
154 | |
155 | If you want to use more XHTML out of the box, then configure your application to extend |
156 | the default skin. |
157 | |
158 | It's worth noting that you are not bound to the XHTML of the default or base skin layouts, |
159 | because you are able to override layout fragments in your own skin. |
160 | |
161 | Next we create C<share/skin/defaults.conf> to allow settings that concern all skins of |
63bb30b4 |
162 | the application. It should contain only this: |
163 | |
164 | widget_search_path MyApp::View::Site::Widget |
165 | widget_search_path Reaction::UI::Widget |
166 | |
167 | This will tell Reaction to look in C<Reaction::UI::Widget::*> and |
168 | C<MyApp::View::Site::Widget::*> for widget classes. That means that our layout named |
169 | C<root> will check for C<MyApp::View::Site::Widget::Root> first and then look if |
170 | C<Reaction::UI::Widget> exists. |
171 | |
172 | We want the first line to be able to create our own widgets and the second line to |
173 | have Reaction find its own widgets. |
174 | |
175 | Now we need to tell Reaction what skin it should use. We do this by adding this section |
176 | to our C<myapp.conf>: |
177 | |
178 | <View Site> |
179 | skin_name myapp |
180 | </View> |
181 | |
182 | The value should be the name of the target directory under C<share/skin/>. |
183 | |
184 | =head1 LAYOUTS |
185 | |
186 | We will need two layout files to begin with. One controlling the site layout and one |
187 | for the root action. |
188 | |
189 | The first will be created as C<share/skin/myapp/layout/site_layout.tt>: |
190 | |
191 | =extends NEXT |
192 | |
193 | =for layout body |
194 | |
195 | <h1>Welcome to MyApp</h1> |
196 | |
197 | <div id="content"> |
198 | [% inner %] |
199 | </div> |
200 | |
201 | =cut |
202 | |
203 | The C<=extends> directive specifies that this layout file is an extension of another |
204 | layout file. The C<NEXT> value here tells Reaction that this extends the C<site_layout> |
205 | layout in the base skin, which we have defined as C</Reaction/default>. That means, you |
206 | can take a look at the layout we are extending at C<share/skin/default/layout/site_layout.tt> |
207 | in the L<Reaction> distribution. |
208 | |
209 | The C<=for layout> directives allows us to set a layout fragment. We define a C<body> fragment |
210 | containing the common C<body> for all pages using this site layout. The C<[% inner %]> is |
211 | where the deeper parts of the stack will be included, in the case of our C<root> action that |
212 | would be the C<Reaction::UI::ViewPort> with the C<root> layout. |
213 | |
753f8f1d |
214 | If we want to override a specific fragment, we can do just that. There are two choices |
215 | when overriding a specific fragment. One can disregard the parent fragment all together |
216 | and start from scratch or they could reuse the parent fragment and add to it. The key to |
217 | re-using a parent (extended) layout fragment is the keyword C<call_next>. Specifically, |
218 | we insert C<[% call_next %]> to include the layout fragment of the parent. |
219 | |
220 | The parent layout fragment is found either in another layout template named in the |
221 | C<=extends> directive or it's in a subclass of Reaction::UI::Widget. |
222 | In the latter case, the Widget subclass name matches the name of the template or |
223 | the widget name posed at the top of the layout file via the C<=widget> directive. |
224 | |
225 | Once you know the hierarchy C<call_next> follows, then you know where to examine |
226 | the details of the parent layouts or widgets. |
63bb30b4 |
227 | |
228 | The layout representing the root action is called C<share/skin/myapp/layout/root.tt>: |
229 | |
230 | =for layout widget |
231 | |
232 | <p>Hello, World!</p> |
233 | |
234 | =cut |
235 | |
236 | This one is rather simple. The C<=for layout widget> directive is special in that the |
237 | C<widget> fragment will always be where the rendering starts. In fact, our C<site_layout> |
238 | layout too contains a C<widget> fragment, you just don't see it because you inherited it from |
239 | your base skin (or your base skin's base skin, for that matter) instead of defining it yourself. |
240 | |
241 | =head1 A SIMPLE WIDGET |
242 | |
243 | If we wanted to use a different kind of widget than that assumed automatically by Reaction, we |
244 | could add a |
245 | |
246 | =widget ClassName |
247 | |
248 | directive at the top of the layout file. But for now, we will instead create our own |
249 | widget at C<lib/MyApp/View/Site/Widget/Root.pm>: |
250 | |
251 | package MyApp::View::Site::Widget::Root; |
252 | use Reaction::UI::WidgetClass; |
253 | |
91c2674b |
254 | use namespace::autoclean; |
63bb30b4 |
255 | |
256 | __PACKAGE__->meta->make_immutable; |
257 | |
258 | 1; |
259 | |
260 | This adds no new functionality at the moment. It just uses C<Reaction::UI::WidgetClass> to ease |
261 | and automate the setup of a new widget class. The widget can provide functionality and fragments |
262 | to the layout. In a way, it can be seen as the Perl code backend to the layout file. |
263 | |
264 | You can now start your C<script/myapp_server.pl> and visit |
265 | |
266 | http://localhost:3000/ |
267 | |
268 | to view your "Hello, World" page. |
269 | |
270 | =head1 ADDING A SCHEMA |
271 | |
272 | The next part of the tutorial will be about adding data storage to our application. While most |
273 | L<Catalyst> web applications today (or at least they should) abstract their database schema |
274 | with L<DBIx::Class::Schema> into a separate module separated from the webapplication, Reaction |
275 | takes this one step further by introducing so called interface models. The interface model |
276 | defines the layer between your application and your domain model (in this case, the L<DBIx::Class> |
277 | schema). |
278 | |
139b74f7 |
279 | The first thing we will need is a schema class in C<lib/MyApp/Schema.pm>: |
63bb30b4 |
280 | |
281 | package MyApp::Schema; |
282 | use strict; |
283 | use warnings; |
284 | |
285 | use parent 'DBIx::Class::Schema'; |
286 | |
287 | __PACKAGE__->load_classes; |
288 | |
289 | 1; |
290 | |
291 | The schema class itself is built like a typical L<DBIx::Class::Schema>. The difference in class |
292 | definition starts at the result classes. For the example's sake, let's make a SQLite database |
293 | called C<example.sqlite>: |
294 | |
295 | $ cat > example.sqlite.sql |
296 | CREATE TABLE foo ( |
297 | id INTEGER PRIMARY KEY AUTOINCREMENT, |
298 | first_name VARCHAR NOT NULL, |
299 | last_name VARCHAR NOT NULL |
300 | ); |
301 | <Ctrl-D> |
302 | |
303 | $ sqlite3 example.sqlite < example.sqlite.sql |
304 | $ |
305 | |
306 | The result class for this table combines the usual style of L<DBIx::Class> with L<Moose> meta |
139b74f7 |
307 | data additions in C<lib/MyApp/Schema/Foo.pm>: |
63bb30b4 |
308 | |
309 | package MyApp::Schema::Foo; |
310 | use Moose; |
311 | use MooseX::Types::Moose qw( Int ); |
312 | use Reaction::Types::Core qw( NonEmptySimpleStr ); |
313 | |
91c2674b |
314 | use namespace::autoclean; |
63bb30b4 |
315 | |
316 | extends 'DBIx::Class'; |
317 | |
318 | has id => |
319 | (is => 'ro', isa => Int, required => 1); |
320 | |
321 | has first_name => |
322 | (is => 'rw', isa => NonEmptySimpleStr, required => 1); |
323 | |
324 | has last_name => |
325 | (is => 'rw', isa => NonEmptySimpleStr, required => 1); |
326 | |
327 | __PACKAGE__->load_components(qw( IntrospectableM2M Core )); |
328 | __PACKAGE__->table('foo'); |
329 | |
330 | __PACKAGE__->add_columns( |
331 | id => { |
332 | data_type => 'integer', |
333 | is_auto_increment => 1, |
334 | }, |
335 | first_name => { data_type => 'varchar' }, |
336 | last_name => { data_type => 'varchar' }, |
337 | ); |
338 | |
339 | __PACKAGE__->set_primary_key('id'); |
340 | |
341 | 1; |
342 | |
343 | The L<MooseX::Types::Moose> and L<Reaction::Types::Core> modules export L<Moose> type |
344 | constraints (See also L<MooseX::Types> and L<Moose::Util::TypeConstraints>). Note that |
345 | we are using L<Moose/extends> here instead of L<base> or L<parent> to extend |
346 | L<DBIx::Class>. |
347 | |
348 | Next we see our columns in form of Moose attribute definitions. The C<is>, C<isa> and |
349 | C<required> attribute parameters will all be used for introspection and interface |
350 | building later. The C<required> is rather straight-forward. The C<is> will decide whether |
351 | this attribute (or column) can be edited (C<ro> means that it can't, C<rw> means it can). |
352 | The C<isa> attribute will be used for validation and rendering of input fields. |
353 | |
354 | The imported C<NonEmptySimpleStr> for example gives us a simple single-line input box, |
355 | while a C<Str> from L<MooseX::Types::Moose> would give us a textbox. |
356 | |
357 | Following that, we have the usual L<DBIx::Class> result class definitions. The only thing |
358 | different might be the new L<DBIx::Class::IntrospectableM2M> which will allow us to |
359 | inspect many-to-many relations later on. |
360 | |
361 | =head1 CREATING AN INTERFACE MODEL |
362 | |
363 | The interface model should be separated from the application and the schema, since it |
364 | will tie both together. In this case, we will use a reflector to set up the usual interface |
139b74f7 |
365 | model actions for our schema (C<Create>, C<Update>, C<Delete>, C<DeleteAll>) in |
366 | C<lib/MyApp/InterfaceModel/DBIC.pm>: |
63bb30b4 |
367 | |
368 | package MyApp::InterfaceModel::DBIC; |
369 | |
370 | # keep this on top |
371 | use parent 'Reaction::InterfaceModel::Object'; |
372 | |
373 | use Reaction::Class; |
374 | use Reaction::InterfaceModel::Reflector::DBIC; |
375 | |
91c2674b |
376 | use namespace::autoclean; |
63bb30b4 |
377 | |
378 | my $reflector = Reaction::InterfaceModel::Reflector::DBIC->new; |
379 | |
380 | $reflector->reflect_schema( |
381 | model_class => __PACKAGE__, |
382 | schema_class => 'MyApp::Schema', |
383 | ); |
384 | |
385 | __PACKAGE__->meta->make_immutable; |
386 | |
387 | 1; |
388 | |
389 | The L<parent> import must happen before the L<Reaction::Class> one in this case. Other |
390 | than that, the only thing we do here is create a new L<Reaction::InterfaceModel::Reflector::DBIC> |
391 | and call C<reflect_schema> to build our C<MyApp::InterfaceModel::DBIC::*> namespace out of our |
392 | C<MyApp::Schema>. |
393 | |
394 | =head1 TIEING THE INTERFACE MODEL TO THE APPLICATION |
395 | |
396 | Next on the list is the integration of our new interface model into our application. For |
397 | this we create a simple catalyst model in C<lib/MyApp/Model/DBIC.pm>: |
398 | |
399 | package MyApp::Model::DBIC; |
400 | use Reaction::Class; |
401 | |
91c2674b |
402 | use namespace::autoclean; |
63bb30b4 |
403 | |
404 | extends 'Catalyst::Model::Reaction::InterfaceModel::DBIC'; |
405 | |
406 | __PACKAGE__->meta->make_immutable; |
407 | |
408 | __PACKAGE__->config( |
409 | im_class => 'MyApp::InterfaceModel::DBIC', |
410 | db_dsn => 'dbi:SQLite:example.sqlite', |
411 | ); |
412 | |
413 | 1; |
414 | |
415 | This model L<extends|Moose> the L<Catalyst::Model::Reaction::InterfaceModel::DBIC> base class |
416 | shipped with Reaction. It's configuration must contain the C<im_class> naming our interface |
0a0813dd |
417 | model and the C<db_dsn>. If you're using a different kind of database then you |
418 | may neeb to add config for C<db_user>, C<db_password>, and C<db_params>. |
419 | All these get passed to the schema's connect() method. |
420 | |
421 | Of course, since this is Catalyst, all this can also easily be specified |
63bb30b4 |
422 | via your application config file under the C<Model::DBIC> key. |
423 | |
424 | =head1 BUILDING A SIMPLE CRUD CONTROLLER |
425 | |
426 | Now, since we have defined our interface model as well as our domain model including meta |
139b74f7 |
427 | data, it isn't very hard (at least not for us) to build a basic (but extendable) |
428 | CRUD controller in C<lib/MyApp/Controller/Foo.pm>: |
63bb30b4 |
429 | |
430 | package MyApp::Controller::Foo; |
431 | use strict; |
432 | use warnings; |
433 | |
434 | use parent 'Reaction::UI::Controller::Collection::CRUD'; |
435 | use Reaction::Class; |
436 | |
91c2674b |
437 | use namespace::autoclean; |
63bb30b4 |
438 | |
439 | __PACKAGE__->config( |
440 | model_name => 'DBIC', |
441 | collection_name => 'Foo', |
442 | actions => { |
443 | base => { Chained => '/base', PathPart => 'foo' }, |
444 | }, |
445 | ); |
446 | |
447 | 1; |
448 | |
449 | This controller subclasses L<Reaction::UI::Controller::Collection::CRUD>, which is itself a |
450 | subclass of L<Reaction::UI::Controller::Collection>, a class to ease the creation of controllers |
451 | who act on collections of things. |
452 | |
453 | As you can see, for the simplest case we don't need any code; we simply configure our controller. |
454 | |
455 | The C<model_name> is the name of our interface model sans the C<MyApp::Model::> prefix. This means |
456 | this entry points to C<MyApp::Model::DBIC> in this case. The C<collection_name> is the name of |
0a0813dd |
457 | the collection in the specified interface model. For us, this would be C<Foo>, to match the result |
63bb30b4 |
458 | class we created above and want to manage. |
459 | |
460 | The C<actions> part of the configuration is not Reaction, but rather Catalyst specific. This |
461 | configures the actions inherited from L<Reaction::UI::Controller::Collection::CRUD>. For it to |
462 | work, we only need to tell the C<base> action where to chain off from, and what C<PathPart> |
463 | to use. We chain it to the C<base> action in our root controller. The C<foo> path part is rather |
464 | obvious. |
465 | |
466 | Now you can restart your application and visit |
467 | |
468 | http://localhost:3000/foo |
469 | |
470 | and you have a complete CRUD interface for C<Foo> with listing, viewing, creating, updating |
471 | and deleting capabilities. |
472 | |
f9b32c83 |
473 | =head1 WHERE TO GO NEXT |
474 | |
475 | =over |
476 | |
477 | =item L<Reaction::Manual::Templates> |
478 | |
479 | When a viewport tries to render a layout, it will involve the view to figure out the corresponding |
480 | template (or any other kind of GUI description) and render it. The template documentation is concerned |
481 | mostly with the C<Reaction::UI::View::TT> implementation allowing the developer to use the L<Template> |
482 | engine to render the layouts. |
483 | |
484 | =item L<Reaction::Manual::Widgets> |
485 | |
486 | A widget is the backend Perl object providing the Perl view logic to a layout. What the rendered |
487 | output actually looks like is determined by the layout. The widget is concerned with storing, providing |
488 | and managing data used and rendered by the layout. |
489 | |
490 | =back |
491 | |
63bb30b4 |
492 | =head1 SEE ALSO |
493 | |
494 | =over |
495 | |
496 | =item * L<Reaction::Manual> |
497 | |
498 | =item * L<Reaction::Manual::Intro> |
499 | |
500 | =item * L<Reaction::Manual::Overview> |
501 | |
502 | =item * L<Reaction::Manual::Templates> |
503 | |
504 | =item * L<Reaction::Manual::RenderPage> |
505 | |
506 | =item * L<Reaction::UI::View::TT> |
507 | |
508 | =item * L<Reaction::UI::Controller::Root> |
509 | |
510 | =item * L<Reaction::UI::WidgetClass> |
511 | |
512 | =back |
513 | |
514 | =head1 AUTHORS |
515 | |
516 | See L<Reaction::Class> for authors. |
517 | |
518 | =head1 LICENSE |
519 | |
520 | See L<Reaction::Class> for the license. |
521 | |
522 | =cut |