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