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