Commit | Line | Data |
df2804f5 |
1 | =head1 NAME |
2 | |
3 | Reaction::Manual::Intro - Introduction to Reaction |
4 | |
5 | =head1 INTRODUCTION |
6 | |
7 | Reaction is basically an extended MVC: |
8 | |
9 | =over |
10 | |
11 | =item Domain Model |
12 | |
13 | DBIx::Class::Schema, MyApp::Foo, MyApp::Bar, etc. |
14 | |
15 | =item Interface Model |
16 | |
17 | InterfaceModel::DBIC::Schema, InterfaceModel::Action, |
18 | MyApp::InterfaceModel::Foo classes. |
19 | |
20 | =item Controller |
21 | |
22 | Mediation and navigation. |
23 | |
24 | =item ViewPort |
25 | |
26 | Event handling encapsulation. |
27 | |
28 | =item Widget |
29 | |
30 | View logic. |
31 | |
32 | =item Renderer |
33 | |
34 | MyApp::View:: classes, renders viewports. |
35 | |
36 | =back |
37 | |
38 | =head1 THE REACTION WAY |
39 | |
40 | The idea is you separate your domain model, which encapsulates the domain |
41 | itself from your interface model, which is a model of how a particular app or |
42 | class of apps interact with that domain and provides objects/methods to |
43 | encapsulate the common operations it does. |
44 | |
45 | =head2 Domain Models vs Interface Models |
46 | |
47 | Domain models are expected to drive the application business logic and data. |
48 | All domain models that need to be effectively displayed somehow at the user |
49 | interface (a table, for instance) must interact with an interface model. |
50 | These should provide the common methods needed in order to carry out |
51 | user-generated events. |
52 | |
53 | =head1 SETTING UP A REACTION APPLICATION |
54 | |
55 | Reaction applications are set up just like Catalyst: |
56 | |
57 | $ catalyst.pl MyApp |
58 | # output ommited |
59 | $ cd MyApp |
60 | |
61 | =head2 Models |
62 | |
63 | Reaction provides a reflector component which automagically |
64 | maps a L<DBIx::Class::Schema> into a set of Interface Models which can be used |
65 | by Reaction to build the interface components. If you're not familiar with |
66 | L<DBIx::Class> or don't have a schema handy, now is a good time to go through |
67 | L<DBIx::Class::Manual::Intro> to get a schema set up. |
68 | |
888532d3 |
69 | It is important that your Result-objects implement the meta-protocol of Moose |
70 | One way to achive that is to do the following: |
71 | |
72 | package MyApp::Schema::Result::Bar; |
73 | use base 'DBIx::Class'; |
74 | use Moose; |
75 | |
159fd246 |
76 | has 'name' => (isa => 'Str', required => 1, rw => 1); |
888532d3 |
77 | |
78 | use namespace::clean -except => [ 'meta' ]; |
79 | |
80 | __PACKAGE__->load_components(qw(Core)); |
81 | __PACKAGE__->table('bar'); |
82 | __PACKAGE__->add_columns( |
83 | name => { |
84 | data_type => 'varchar', |
85 | size => 255, |
86 | is_nullable => 0, |
87 | } |
88 | ); |
89 | __PACKAGE__->primary_key('name'); |
90 | 1; |
91 | |
92 | Once you have your schema set up like that, you can create the InferfaceModel: |
93 | |
df2804f5 |
94 | package MyApp::InterfaceModel::DBIC; |
95 | |
96 | use base 'Reaction::InterfaceModel::Object'; |
97 | use Reaction::InterfaceModel::Reflector::DBIC; |
98 | |
99 | my $reflector = Reaction::InterfaceModel::Reflector::DBIC->new; |
100 | |
101 | $reflector->reflect_schema( |
102 | model_class => __PACKAGE__, |
103 | schema_class => 'MyApp::Schema', |
104 | sources => [qw/Foo Baz/], |
105 | ); |
106 | |
107 | 1; |
108 | |
888532d3 |
109 | Then you create a MyApp::Model that uses this InferfaceModel: |
110 | |
111 | package Myapp::Model::IM; |
112 | |
888532d3 |
113 | use Reaction::Class; |
114 | |
2b14a136 |
115 | extends 'Catalyst::Model::Reaction::InterfaceModel::DBIC'; |
888532d3 |
116 | |
117 | 1; |
118 | |
df2804f5 |
119 | =head2 Controllers |
120 | |
888532d3 |
121 | =head3 Root controller |
122 | |
df2804f5 |
123 | Your Reaction application must have a Root controller which inherits from |
124 | C<Reaction::UI::Controller::Root>. |
125 | |
126 | package MyApp::Controller::Root; |
127 | |
128 | use warnings; |
129 | use strict; |
130 | use base qw/Reaction::UI::Controller::Root/; |
131 | |
132 | __PACKAGE__->config( |
133 | view_name => 'Site', |
134 | window_title => 'My Reaction App', |
135 | namespace => '' |
136 | ); |
137 | |
5a333e30 |
138 | sub base : Chained('/') PathPart('') CaptureArgs(0) { |
139 | # do some setup for every request |
140 | # also provides a chain root for other controllers to use |
141 | } |
142 | |
df2804f5 |
143 | 1; |
144 | |
888532d3 |
145 | =head3 Individual controllers |
146 | |
147 | For each Collection(table?) in your DB, you need to create a controller |
148 | |
149 | package MyApp::Controller::Foo; |
150 | |
151 | use base 'Reaction::UI::Controller::Collection::CRUD'; |
152 | use Reaction::Class; |
153 | |
154 | __PACKAGE__->config( |
155 | model_name => 'IM', # This corresponds to the name of the MyApp::Model you created earlier |
156 | collection_name => 'Foo', # Name of one of the sources in your InterfaceModel |
5a333e30 |
157 | action => { |
158 | base => { Chained => '/base', # chain to the base action in the root controller |
159 | PathPart => 'foo' }, |
160 | }, |
888532d3 |
161 | ); |
162 | |
163 | 1; |
164 | |
df2804f5 |
165 | XX TODO |
166 | |
167 | =head2 View |
168 | |
888532d3 |
169 | One of the views in your application should look something like this: |
170 | |
171 | package MyApp::View::TT; |
172 | |
173 | use Reaction::Class; |
174 | |
2b14a136 |
175 | extends 'Reaction::UI::View::TT'; |
888532d3 |
176 | |
177 | 1; |
178 | |
179 | __END__; |
180 | |
181 | |
df2804f5 |
182 | XX TODO |
183 | |
184 | =head1 SEE ALSO |
185 | |
186 | =over |
187 | |
188 | =item * L<Reaction::Manual::Cookbook> |
189 | |
190 | =item * L<Reaction::Manual::FAQ> |
191 | |
192 | =back |
193 | |
194 | =head1 AUTHORS |
195 | |
196 | See L<Reaction::Class> for authors. |
197 | |
198 | =head1 LICENSE |
199 | |
200 | See L<Reaction::Class> for the license. |
201 | |
202 | =cut |