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