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 | |
145 | Now we create C<share/skin/defaults.conf> to allow settings that concern all skins of |
146 | the application. It should contain only this: |
147 | |
148 | widget_search_path MyApp::View::Site::Widget |
149 | widget_search_path Reaction::UI::Widget |
150 | |
151 | This will tell Reaction to look in C<Reaction::UI::Widget::*> and |
152 | C<MyApp::View::Site::Widget::*> for widget classes. That means that our layout named |
153 | C<root> will check for C<MyApp::View::Site::Widget::Root> first and then look if |
154 | C<Reaction::UI::Widget> exists. |
155 | |
156 | We want the first line to be able to create our own widgets and the second line to |
157 | have Reaction find its own widgets. |
158 | |
159 | Now we need to tell Reaction what skin it should use. We do this by adding this section |
160 | to our C<myapp.conf>: |
161 | |
162 | <View Site> |
163 | skin_name myapp |
164 | </View> |
165 | |
166 | The value should be the name of the target directory under C<share/skin/>. |
167 | |
168 | =head1 LAYOUTS |
169 | |
170 | We will need two layout files to begin with. One controlling the site layout and one |
171 | for the root action. |
172 | |
173 | The first will be created as C<share/skin/myapp/layout/site_layout.tt>: |
174 | |
175 | =extends NEXT |
176 | |
177 | =for layout body |
178 | |
179 | <h1>Welcome to MyApp</h1> |
180 | |
181 | <div id="content"> |
182 | [% inner %] |
183 | </div> |
184 | |
185 | =cut |
186 | |
187 | The C<=extends> directive specifies that this layout file is an extension of another |
188 | layout file. The C<NEXT> value here tells Reaction that this extends the C<site_layout> |
189 | layout in the base skin, which we have defined as C</Reaction/default>. That means, you |
190 | can take a look at the layout we are extending at C<share/skin/default/layout/site_layout.tt> |
191 | in the L<Reaction> distribution. |
192 | |
193 | The C<=for layout> directives allows us to set a layout fragment. We define a C<body> fragment |
194 | containing the common C<body> for all pages using this site layout. The C<[% inner %]> is |
195 | where the deeper parts of the stack will be included, in the case of our C<root> action that |
196 | would be the C<Reaction::UI::ViewPort> with the C<root> layout. |
197 | |
198 | If we wanted to override a specific fragment, we could do just that. And inside that fragment |
1639407f |
199 | we could call C<[% call_next %]> to include the layout fragment from the extended layout. |
63bb30b4 |
200 | |
201 | The layout representing the root action is called C<share/skin/myapp/layout/root.tt>: |
202 | |
203 | =for layout widget |
204 | |
205 | <p>Hello, World!</p> |
206 | |
207 | =cut |
208 | |
209 | This one is rather simple. The C<=for layout widget> directive is special in that the |
210 | C<widget> fragment will always be where the rendering starts. In fact, our C<site_layout> |
211 | layout too contains a C<widget> fragment, you just don't see it because you inherited it from |
212 | your base skin (or your base skin's base skin, for that matter) instead of defining it yourself. |
213 | |
214 | =head1 A SIMPLE WIDGET |
215 | |
216 | If we wanted to use a different kind of widget than that assumed automatically by Reaction, we |
217 | could add a |
218 | |
219 | =widget ClassName |
220 | |
221 | directive at the top of the layout file. But for now, we will instead create our own |
222 | widget at C<lib/MyApp/View/Site/Widget/Root.pm>: |
223 | |
224 | package MyApp::View::Site::Widget::Root; |
225 | use Reaction::UI::WidgetClass; |
226 | |
91c2674b |
227 | use namespace::autoclean; |
63bb30b4 |
228 | |
229 | __PACKAGE__->meta->make_immutable; |
230 | |
231 | 1; |
232 | |
233 | This adds no new functionality at the moment. It just uses C<Reaction::UI::WidgetClass> to ease |
234 | and automate the setup of a new widget class. The widget can provide functionality and fragments |
235 | to the layout. In a way, it can be seen as the Perl code backend to the layout file. |
236 | |
237 | You can now start your C<script/myapp_server.pl> and visit |
238 | |
239 | http://localhost:3000/ |
240 | |
241 | to view your "Hello, World" page. |
242 | |
243 | =head1 ADDING A SCHEMA |
244 | |
245 | The next part of the tutorial will be about adding data storage to our application. While most |
246 | L<Catalyst> web applications today (or at least they should) abstract their database schema |
247 | with L<DBIx::Class::Schema> into a separate module separated from the webapplication, Reaction |
248 | takes this one step further by introducing so called interface models. The interface model |
249 | defines the layer between your application and your domain model (in this case, the L<DBIx::Class> |
250 | schema). |
251 | |
139b74f7 |
252 | The first thing we will need is a schema class in C<lib/MyApp/Schema.pm>: |
63bb30b4 |
253 | |
254 | package MyApp::Schema; |
255 | use strict; |
256 | use warnings; |
257 | |
258 | use parent 'DBIx::Class::Schema'; |
259 | |
260 | __PACKAGE__->load_classes; |
261 | |
262 | 1; |
263 | |
264 | The schema class itself is built like a typical L<DBIx::Class::Schema>. The difference in class |
265 | definition starts at the result classes. For the example's sake, let's make a SQLite database |
266 | called C<example.sqlite>: |
267 | |
268 | $ cat > example.sqlite.sql |
269 | CREATE TABLE foo ( |
270 | id INTEGER PRIMARY KEY AUTOINCREMENT, |
271 | first_name VARCHAR NOT NULL, |
272 | last_name VARCHAR NOT NULL |
273 | ); |
274 | <Ctrl-D> |
275 | |
276 | $ sqlite3 example.sqlite < example.sqlite.sql |
277 | $ |
278 | |
279 | The result class for this table combines the usual style of L<DBIx::Class> with L<Moose> meta |
139b74f7 |
280 | data additions in C<lib/MyApp/Schema/Foo.pm>: |
63bb30b4 |
281 | |
282 | package MyApp::Schema::Foo; |
283 | use Moose; |
284 | use MooseX::Types::Moose qw( Int ); |
285 | use Reaction::Types::Core qw( NonEmptySimpleStr ); |
286 | |
91c2674b |
287 | use namespace::autoclean; |
63bb30b4 |
288 | |
289 | extends 'DBIx::Class'; |
290 | |
291 | has id => |
292 | (is => 'ro', isa => Int, required => 1); |
293 | |
294 | has first_name => |
295 | (is => 'rw', isa => NonEmptySimpleStr, required => 1); |
296 | |
297 | has last_name => |
298 | (is => 'rw', isa => NonEmptySimpleStr, required => 1); |
299 | |
300 | __PACKAGE__->load_components(qw( IntrospectableM2M Core )); |
301 | __PACKAGE__->table('foo'); |
302 | |
303 | __PACKAGE__->add_columns( |
304 | id => { |
305 | data_type => 'integer', |
306 | is_auto_increment => 1, |
307 | }, |
308 | first_name => { data_type => 'varchar' }, |
309 | last_name => { data_type => 'varchar' }, |
310 | ); |
311 | |
312 | __PACKAGE__->set_primary_key('id'); |
313 | |
314 | 1; |
315 | |
316 | The L<MooseX::Types::Moose> and L<Reaction::Types::Core> modules export L<Moose> type |
317 | constraints (See also L<MooseX::Types> and L<Moose::Util::TypeConstraints>). Note that |
318 | we are using L<Moose/extends> here instead of L<base> or L<parent> to extend |
319 | L<DBIx::Class>. |
320 | |
321 | Next we see our columns in form of Moose attribute definitions. The C<is>, C<isa> and |
322 | C<required> attribute parameters will all be used for introspection and interface |
323 | building later. The C<required> is rather straight-forward. The C<is> will decide whether |
324 | this attribute (or column) can be edited (C<ro> means that it can't, C<rw> means it can). |
325 | The C<isa> attribute will be used for validation and rendering of input fields. |
326 | |
327 | The imported C<NonEmptySimpleStr> for example gives us a simple single-line input box, |
328 | while a C<Str> from L<MooseX::Types::Moose> would give us a textbox. |
329 | |
330 | Following that, we have the usual L<DBIx::Class> result class definitions. The only thing |
331 | different might be the new L<DBIx::Class::IntrospectableM2M> which will allow us to |
332 | inspect many-to-many relations later on. |
333 | |
334 | =head1 CREATING AN INTERFACE MODEL |
335 | |
336 | The interface model should be separated from the application and the schema, since it |
337 | will tie both together. In this case, we will use a reflector to set up the usual interface |
139b74f7 |
338 | model actions for our schema (C<Create>, C<Update>, C<Delete>, C<DeleteAll>) in |
339 | C<lib/MyApp/InterfaceModel/DBIC.pm>: |
63bb30b4 |
340 | |
341 | package MyApp::InterfaceModel::DBIC; |
342 | |
343 | # keep this on top |
344 | use parent 'Reaction::InterfaceModel::Object'; |
345 | |
346 | use Reaction::Class; |
347 | use Reaction::InterfaceModel::Reflector::DBIC; |
348 | |
91c2674b |
349 | use namespace::autoclean; |
63bb30b4 |
350 | |
351 | my $reflector = Reaction::InterfaceModel::Reflector::DBIC->new; |
352 | |
353 | $reflector->reflect_schema( |
354 | model_class => __PACKAGE__, |
355 | schema_class => 'MyApp::Schema', |
356 | ); |
357 | |
358 | __PACKAGE__->meta->make_immutable; |
359 | |
360 | 1; |
361 | |
362 | The L<parent> import must happen before the L<Reaction::Class> one in this case. Other |
363 | than that, the only thing we do here is create a new L<Reaction::InterfaceModel::Reflector::DBIC> |
364 | and call C<reflect_schema> to build our C<MyApp::InterfaceModel::DBIC::*> namespace out of our |
365 | C<MyApp::Schema>. |
366 | |
367 | =head1 TIEING THE INTERFACE MODEL TO THE APPLICATION |
368 | |
369 | Next on the list is the integration of our new interface model into our application. For |
370 | this we create a simple catalyst model in C<lib/MyApp/Model/DBIC.pm>: |
371 | |
372 | package MyApp::Model::DBIC; |
373 | use Reaction::Class; |
374 | |
91c2674b |
375 | use namespace::autoclean; |
63bb30b4 |
376 | |
377 | extends 'Catalyst::Model::Reaction::InterfaceModel::DBIC'; |
378 | |
379 | __PACKAGE__->meta->make_immutable; |
380 | |
381 | __PACKAGE__->config( |
382 | im_class => 'MyApp::InterfaceModel::DBIC', |
383 | db_dsn => 'dbi:SQLite:example.sqlite', |
384 | ); |
385 | |
386 | 1; |
387 | |
388 | This model L<extends|Moose> the L<Catalyst::Model::Reaction::InterfaceModel::DBIC> base class |
389 | shipped with Reaction. It's configuration must contain the C<im_class> naming our interface |
0a0813dd |
390 | model and the C<db_dsn>. If you're using a different kind of database then you |
391 | may neeb to add config for C<db_user>, C<db_password>, and C<db_params>. |
392 | All these get passed to the schema's connect() method. |
393 | |
394 | Of course, since this is Catalyst, all this can also easily be specified |
63bb30b4 |
395 | via your application config file under the C<Model::DBIC> key. |
396 | |
397 | =head1 BUILDING A SIMPLE CRUD CONTROLLER |
398 | |
399 | Now, since we have defined our interface model as well as our domain model including meta |
139b74f7 |
400 | data, it isn't very hard (at least not for us) to build a basic (but extendable) |
401 | CRUD controller in C<lib/MyApp/Controller/Foo.pm>: |
63bb30b4 |
402 | |
403 | package MyApp::Controller::Foo; |
404 | use strict; |
405 | use warnings; |
406 | |
407 | use parent 'Reaction::UI::Controller::Collection::CRUD'; |
408 | use Reaction::Class; |
409 | |
91c2674b |
410 | use namespace::autoclean; |
63bb30b4 |
411 | |
412 | __PACKAGE__->config( |
413 | model_name => 'DBIC', |
414 | collection_name => 'Foo', |
415 | actions => { |
416 | base => { Chained => '/base', PathPart => 'foo' }, |
417 | }, |
418 | ); |
419 | |
420 | 1; |
421 | |
422 | This controller subclasses L<Reaction::UI::Controller::Collection::CRUD>, which is itself a |
423 | subclass of L<Reaction::UI::Controller::Collection>, a class to ease the creation of controllers |
424 | who act on collections of things. |
425 | |
426 | As you can see, for the simplest case we don't need any code; we simply configure our controller. |
427 | |
428 | The C<model_name> is the name of our interface model sans the C<MyApp::Model::> prefix. This means |
429 | this entry points to C<MyApp::Model::DBIC> in this case. The C<collection_name> is the name of |
0a0813dd |
430 | the collection in the specified interface model. For us, this would be C<Foo>, to match the result |
63bb30b4 |
431 | class we created above and want to manage. |
432 | |
433 | The C<actions> part of the configuration is not Reaction, but rather Catalyst specific. This |
434 | configures the actions inherited from L<Reaction::UI::Controller::Collection::CRUD>. For it to |
435 | work, we only need to tell the C<base> action where to chain off from, and what C<PathPart> |
436 | to use. We chain it to the C<base> action in our root controller. The C<foo> path part is rather |
437 | obvious. |
438 | |
439 | Now you can restart your application and visit |
440 | |
441 | http://localhost:3000/foo |
442 | |
443 | and you have a complete CRUD interface for C<Foo> with listing, viewing, creating, updating |
444 | and deleting capabilities. |
445 | |
f9b32c83 |
446 | =head1 WHERE TO GO NEXT |
447 | |
448 | =over |
449 | |
450 | =item L<Reaction::Manual::Templates> |
451 | |
452 | When a viewport tries to render a layout, it will involve the view to figure out the corresponding |
453 | template (or any other kind of GUI description) and render it. The template documentation is concerned |
454 | mostly with the C<Reaction::UI::View::TT> implementation allowing the developer to use the L<Template> |
455 | engine to render the layouts. |
456 | |
457 | =item L<Reaction::Manual::Widgets> |
458 | |
459 | A widget is the backend Perl object providing the Perl view logic to a layout. What the rendered |
460 | output actually looks like is determined by the layout. The widget is concerned with storing, providing |
461 | and managing data used and rendered by the layout. |
462 | |
463 | =back |
464 | |
63bb30b4 |
465 | =head1 SEE ALSO |
466 | |
467 | =over |
468 | |
469 | =item * L<Reaction::Manual> |
470 | |
471 | =item * L<Reaction::Manual::Intro> |
472 | |
473 | =item * L<Reaction::Manual::Overview> |
474 | |
475 | =item * L<Reaction::Manual::Templates> |
476 | |
477 | =item * L<Reaction::Manual::RenderPage> |
478 | |
479 | =item * L<Reaction::UI::View::TT> |
480 | |
481 | =item * L<Reaction::UI::Controller::Root> |
482 | |
483 | =item * L<Reaction::UI::WidgetClass> |
484 | |
485 | =back |
486 | |
487 | =head1 AUTHORS |
488 | |
489 | See L<Reaction::Class> for authors. |
490 | |
491 | =head1 LICENSE |
492 | |
493 | See L<Reaction::Class> for the license. |
494 | |
495 | =cut |