Commit | Line | Data |
7adfd53f |
1 | =head1 NAME |
2 | |
3 | Reaction::Manual::Example - Simple Reaction example |
4 | |
5 | =head1 DESCRIPTION |
6 | |
7 | This tutorial will guide you through the process of setting up and testing a |
8 | very basic CRUD application based on the database from |
9 | L<DBIx::Class::Manual::Example>. |
10 | |
11 | You need at least a fairly basic understanding of L<DBIx::Class::Schema> for |
12 | this example to have value for you. |
13 | |
14 | =head2 Installation |
15 | |
16 | Install L<DBIx::Class> via CPAN. |
17 | |
18 | Install Reaction from http://code2.0beta.co.uk/reaction/svn via SVN or SVK. |
19 | |
20 | Set up the database as mentioned in L<DBIx::Class::Manual::Example>. Don't do |
21 | any of the DBIx::Class related stuff, only the SQLite database. |
22 | |
23 | =head2 Create the application |
24 | |
25 | catalyst.pl Test::Reaction |
26 | cd Test-Reaction |
27 | script/test_reaction_create.pl Model Test::Reaction DBIC::Schema Test::Reaction::DB |
28 | |
29 | Also, remember to include Catalyst::Plugin::I18N in your plugin list, like |
30 | this: |
31 | |
32 | use Catalyst qw/-Debug ConfigLoader Static::Simple I18N/; |
33 | |
34 | =head2 Set up DBIx::Class::Schema |
35 | |
36 | In addition to the normal DBIC stuff, you need to moosify your DBIC classes. |
37 | |
38 | Change directory back from db to the directory app: |
39 | |
40 | cd lib/Test/Reaction |
41 | mkdir DB |
42 | |
43 | Then, create the following DBIx::Class::Schema classes: |
44 | |
45 | DB.pm: |
46 | |
47 | package Test::Reaction::DB; |
48 | |
49 | use base 'DBIx::Class::Schema'; |
50 | |
51 | __PACKAGE__->load_classes; |
52 | |
53 | 1; |
54 | |
55 | DB/Artist.pm: |
56 | |
57 | package Test::Reaction::DB::Artist; |
58 | |
59 | use base 'DBIx::Class'; |
60 | use Reaction::Class; |
61 | |
62 | has 'artistid' => ( isa => 'Int', is => 'ro', required => 1 ); |
63 | has 'name' => ( isa => 'NonEmptySimpleStr', is => 'rw', required => 1 ); |
64 | |
65 | sub display_name { |
66 | my $self = shift; |
67 | return $self->name; |
68 | } |
69 | |
70 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
71 | __PACKAGE__->table('artist'); |
72 | __PACKAGE__->add_columns(qw/ artistid name /); |
73 | __PACKAGE__->set_primary_key('artistid'); |
74 | __PACKAGE__->has_many( 'cds' => 'Test::Reaction::DB::Cd' ); |
75 | |
76 | 1; |
77 | |
78 | DB/Cd.pm: |
79 | |
80 | package Test::Reaction::DB::Cd; |
81 | |
82 | use base 'DBIx::Class'; |
83 | use Reaction::Class; |
84 | |
85 | has 'cdid' => ( isa => 'Int', is => 'ro', required => 1 ); |
86 | has 'artist' => |
87 | ( isa => 'Test::Reaction::DB::Artist', is => 'rw', required => 1 ); |
88 | has 'title' => ( isa => 'NonEmptySimpleStr', is => 'rw', required => 1 ); |
89 | |
90 | sub display_name { |
91 | my $self = shift; |
92 | return $self->title; |
93 | } |
94 | |
95 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
96 | __PACKAGE__->table('cd'); |
97 | __PACKAGE__->add_columns(qw/ cdid artist title/); |
98 | __PACKAGE__->set_primary_key('cdid'); |
99 | __PACKAGE__->belongs_to( 'artist' => 'Test::Reaction::DB::Artist' ); |
100 | __PACKAGE__->has_many( 'tracks' => 'Test::Reaction::DB::Track' ); |
101 | |
102 | 1; |
103 | |
104 | DB/Track.pm: |
105 | |
106 | package Test::Reaction::DB::Track; |
107 | |
108 | use base 'DBIx::Class'; |
109 | use Reaction::Class; |
110 | |
111 | has 'trackid' => ( isa => 'Int', is => 'ro', required => 1 ); |
112 | has 'cd' => ( isa => 'Test::Reaction::DB::Cd', is => 'rw', required => 1 ); |
113 | has 'title' => ( isa => 'NonEmptySimpleStr', is => 'rw', required => 1 ); |
114 | |
115 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
116 | __PACKAGE__->table('track'); |
117 | __PACKAGE__->add_columns(qw/ trackid cd title/); |
118 | __PACKAGE__->set_primary_key('trackid'); |
119 | __PACKAGE__->belongs_to( 'cd' => 'Test::Reaction::DB::Cd' ); |
120 | |
121 | 1; |
122 | |
123 | =head3 Reaction attributes |
124 | |
125 | See L<Reaction::Types::Core> |
126 | |
127 | =head3 The rest |
128 | |
129 | Reaction will use I<sub display_name> for displaying when there is a 1:Many or |
130 | Many:Many relation. It will return a suitable text representation. |
131 | |
132 | =head2 Models |
133 | |
134 | =head3 Create Test::Reaction::Model::Action |
135 | |
136 | Still in lib/Test/Reaction, create |
137 | |
138 | Model/Action.pm: |
139 | |
140 | package Test::Reaction::Model::Action; |
141 | |
142 | use Reaction::Class; |
143 | |
144 | use Test::Reaction::DB; |
145 | |
146 | use aliased 'Reaction::InterfaceModel::Action::DBIC::ActionReflector'; |
147 | |
148 | my $r = ActionReflector->new; |
149 | |
150 | $r->reflect_actions_for( 'Test::Reaction::DB::Artist' => __PACKAGE__ ); |
151 | $r->reflect_actions_for( 'Test::Reaction::DB::Cd' => __PACKAGE__ ); |
152 | $r->reflect_actions_for( 'Test::Reaction::DB::Track' => __PACKAGE__ ); |
153 | |
154 | 1; |
155 | |
156 | =head2 Controllers |
157 | |
158 | Reaction controllers inherit from Reaction::UI::CRUDController, like this: |
159 | |
160 | Controller/Artist.pm |
161 | |
162 | package Test::Reaction::Controller::Artist; |
163 | |
164 | use strict; |
165 | use warnings; |
166 | use base 'Reaction::UI::CRUDController'; |
167 | use Reaction::Class; |
168 | |
169 | __PACKAGE__->config( |
170 | model_base => 'Test::Reaction', |
171 | model_name => 'Artist', |
172 | action => { base => { Chained => '/base', PathPart => 'artist' } } |
173 | ); |
174 | |
175 | 1; |
176 | |
177 | Controller/Cd.pm |
178 | |
179 | package Test::Reaction::Controller::Cd; |
180 | |
181 | use strict; |
182 | use warnings; |
183 | use base 'Reaction::UI::CRUDController'; |
184 | use Reaction::Class; |
185 | |
186 | __PACKAGE__->config( |
187 | model_base => 'Test::Reaction', |
188 | model_name => 'Cd', |
189 | action => { base => { Chained => '/base', PathPart => 'cd' } } |
190 | ); |
191 | |
192 | 1; |
193 | |
194 | Controller/Track.pm |
195 | |
196 | package Test::Reaction::Controller::Track; |
197 | |
198 | use strict; |
199 | use warnings; |
200 | use base 'Reaction::UI::CRUDController'; |
201 | use Reaction::Class; |
202 | |
203 | __PACKAGE__->config( |
204 | model_base => 'Test::Reaction', |
205 | model_name => 'Track', |
206 | action => { base => { Chained => '/base', PathPart => 'track' } } |
207 | ); |
208 | |
209 | 1; |
210 | |
211 | Finally, change Controller/Root.pm to |
212 | |
213 | package Test::Reaction::Controller::Root; |
214 | |
215 | use strict; |
216 | use warnings; |
217 | use base 'Reaction::UI::RootController'; |
218 | use Reaction::Class; |
219 | |
220 | use aliased 'Reaction::UI::ViewPort'; |
221 | use aliased 'Reaction::UI::ViewPort::ListView'; |
222 | use aliased 'Reaction::UI::ViewPort::ActionForm'; |
223 | |
224 | __PACKAGE__->config->{namespace} = ''; |
225 | |
226 | sub base :Chained('/') :PathPart('') :CaptureArgs(0) { |
227 | my ($self, $c) = @_; |
228 | |
229 | $self->push_viewport(ViewPort, layout => 'xhtml'); |
230 | } |
231 | |
232 | sub root :Chained('base') :PathPart('') :Args(0) { |
233 | my ($self, $c) = @_; |
234 | |
235 | $self->push_viewport(ViewPort, layout => 'index'); |
236 | } |
237 | |
238 | 1; |
239 | |
240 | =head2 View |
241 | |
242 | View/XHTML.pm looks like this |
243 | |
244 | package Test::Reaction::View::XHTML; |
245 | |
246 | use Reaction::Class; |
247 | |
248 | extends 'Reaction::UI::Renderer::XHTML'; |
249 | |
250 | 1; |
251 | |
252 | This is all the perly stuff. Now return to the base Test-Reaction directory and |
253 | create root/index: |
254 | |
255 | [% |
256 | |
257 | main_block = 'index'; |
258 | |
259 | BLOCK index; |
260 | |
261 | %]<p><a href="[% ctx.uri_for('/artist') %]">artist</a></p> |
262 | <p><a href="[% ctx.uri_for('/cd') %]">cd</a></p> |
263 | <p><a href="[% ctx.uri_for('/track') %]">track</a></p>[% |
264 | |
265 | END; |
266 | |
267 | %] |
268 | |
269 | =head2 Running |
270 | |
271 | Now all that remains is to tell catalyst about the root and the model. Let |
272 | test_reaction.yml look like this: |
273 | |
274 | --- |
275 | name: Test::Reaction |
276 | Controller::Root: |
277 | view_name: 'XHTML' |
278 | window_title: 'Reaction Test App' |
279 | Model::Test::Reaction: |
280 | schema_class: 'Test::Reaction::DB' |
281 | connect_info: |
282 | - 'dbi:SQLite:dbname=database/example.db' |
283 | |
284 | The finals step for this example is to link to Reaction's templates: |
285 | |
286 | ln -s <path to reaction install directory>/root/base/ root/base |
287 | |
288 | At last you're now ready to run the server |
289 | |
290 | script/test_reaction_server.pl |
291 | |
292 | =head1 Notes |
293 | |
294 | =head1 TODO |
295 | |
296 | =head1 AUTHORS |
297 | |
298 | See L<Reaction::Class> for authors. |
299 | |
300 | =head1 LICENSE |
301 | |
302 | See L<Reaction::Class> for the license. |
303 | |
304 | =cut |