POD FTW
[catagits/Reaction.git] / lib / Reaction / UI / Controller / Collection.pm
CommitLineData
89b70ba7 1package Reaction::UI::Controller::Collection;
2
3use strict;
4use warnings;
5use base 'Reaction::UI::Controller';
6use Reaction::Class;
7
8cc0103d 8use aliased 'Reaction::UI::ViewPort::Collection::Grid';
c8fbb8ad 9use aliased 'Reaction::UI::ViewPort::Object';
89b70ba7 10
37728bba 11has model_name => (isa => 'Str', is => 'rw', required => 1);
12has collection_name => (isa => 'Str', is => 'rw', required => 1);
89b70ba7 13
37728bba 14has action_viewport_map => (isa => 'HashRef', is => 'rw', lazy_build => 1);
89b70ba7 15has action_viewport_args => (isa => 'HashRef', is => 'rw', lazy_build => 1);
16
37728bba 17has default_member_actions => (
18 isa => 'ArrayRef',
19 is => 'rw',
20 lazy_build => 1
21);
22
23has default_collection_actions => (
24 isa => 'ArrayRef',
25 is => 'rw',
26 lazy_build => 1
27);
28
29sub _build_default_member_actions { ['view'] }
30
31sub _build_default_collection_actions { [] }
32
89b70ba7 33sub _build_action_viewport_map {
37728bba 34 my $self = shift;
35 my %map;
36 $map{list} = Grid;
b3cb974a 37 $map{view} = Object; #if grep {$_ eq 'view'} @{$self->default_member_actions};
37728bba 38 return \%map;
89b70ba7 39}
40
41sub _build_action_viewport_args {
37728bba 42 my $self = shift;
43 my $args = { list => { Member => {} } };
44
45 my $m_protos = $args->{list}{Member}{action_prototypes} = {};
46 for my $action_name( @{ $self->default_member_actions }){
47 my $label = ucfirst(join(' ', split(/_/, $action_name)));
48 my $proto = $self->_build_member_action_prototype($label, $action_name);
49 $m_protos->{$action_name} = $proto;
50 }
51
52 my $c_protos = $args->{list}{action_prototypes} = {};
53 for my $action_name( @{ $self->default_collection_actions }){
54 my $label = ucfirst(join(' ', split(/_/, $action_name)));
55 my $proto = $self->_build_collection_action_prototype($label, $action_name);
56 $c_protos->{$action_name} = $proto;
57 }
58
59 return $args;
60}
61
62sub _build_member_action_prototype {
63 my ($self, $label, $action_name) = @_;
64 return {
65 label => $label,
66 uri => sub {
67 my $action = $self->action_for($action_name);
68 $_[1]->uri_for($action, [ @{$_[1]->req->captures}, $_[0]->__id ]);
69 },
70 };
71}
72
73sub _build_collection_action_prototype {
74 my ($self, $label, $action_name) = @_;
75 return {
76 label => $label,
77 uri => sub {
78 my $action = $self->action_for($action_name);
79 $_[1]->uri_for($action, $_[1]->req->captures);
80 },
81 };
89b70ba7 82}
83
89b70ba7 84#XXX candidate for futre optimization, should cache reader?
85sub get_collection {
86 my ($self, $c) = @_;
87 my $model = $c->model( $self->model_name );
0ccdac9b 88 my $collection = $self->collection_name;
89 if( my $meth = $model->can( $collection ) ){
90 return $model->$meth;
91 } elsif ( my $attr = $model->meta->find_attribute_by_name($collection) ) {
92 my $reader = $attr->get_read_method;
93 return $model->$reader;
94 }
95 confess "Failed to find collection $collection";
89b70ba7 96}
97
b3832dbc 98sub base :Action :CaptureArgs(0) {
89b70ba7 99 my ($self, $c) = @_;
89b70ba7 100}
101
102sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
103 my ($self, $c, $key) = @_;
1810d302 104 my $object = $self->get_collection($c)->find($key);
cb92a3a3 105 $c->detach("/error_404") unless $object;
1810d302 106 $c->stash(object => $object);
89b70ba7 107}
108
b3832dbc 109sub list :Chained('base') :PathPart('') :Args(0) {
110 my ($self, $c) = @_;
b68d6a35 111 $self->basic_page($c, { collection => $self->get_collection($c) });
b3832dbc 112}
113
89b70ba7 114sub view :Chained('object') :Args(0) {
115 my ($self, $c) = @_;
b68d6a35 116 $self->basic_page($c, { model => $c->stash->{object} });
89b70ba7 117}
118
aee256be 119sub basic_page {
89b70ba7 120 my ($self, $c, $vp_args) = @_;
aee256be 121 my $action_name = $c->stack->[-1]->name;
380db949 122 my $vp = $self->action_viewport_map->{$action_name},
123 my $args = $self->merge_config_hashes
89b70ba7 124 (
380db949 125 $vp_args || {},
126 $self->action_viewport_args->{$action_name} || {} ,
89b70ba7 127 );
380db949 128 return $self->push_viewport($vp, %$args);
89b70ba7 129}
130
1311;
b3832dbc 132
b3832dbc 133__END__;
134
135=head1 NAME
136
7d3fe0d2 137Reaction::UI::Controller
b3832dbc 138
139=head1 DESCRIPTION
140
141Controller class used to make displaying collections easier.
142Inherits from L<Reaction::UI::Controller>.
143
144=head1 ATTRIBUTES
145
146=head2 model_name
147
0ccdac9b 148The name of the model this controller will use as it's data source. Should be a
7d3fe0d2 149name that can be passed to C<$C-E<gt>model>
b3832dbc 150
151=head2 collection_name
152
0ccdac9b 153The name of the collection whithin the model that this Controller will be
7d3fe0d2 154utilizing.
b3832dbc 155
156=head2 action_viewport_map
157
158=over 4
159
160=item B<_build_action_viewport_map> - Provided builder method, see METHODS
161
162=item B<has_action_viewport_map> - Auto generated predicate
163
164=item B<clear_action_viewport_map>- Auto generated clearer method
165
166=back
167
0ccdac9b 168Read-write lazy building hashref. The keys should match action names in the
169Controller and the value should be the ViewPort class that this action should
7d3fe0d2 170use. See method C<basic_page> for more info.
b3832dbc 171
de5b3fab 172=head2 action_viewport_args
b3832dbc 173
0ccdac9b 174Read-write lazy building hashref. Additional ViewPort arguments for the action
7d3fe0d2 175named as the key in the controller. See method C<basic_page> for more info.
b3832dbc 176
177=over 4
178
179=item B<_build_action_viewport_args> - Provided builder method, see METHODS
180
181=item B<has_action_viewport_args> - Auto generated predicate
182
183=item B<clear_action_viewport_args>- Auto generated clearer method
184
185=back
186
b3cb974a 187=head2 default_member_actions
188
189Read-write lazy building arrayref. The names of the member actions (the actions
190that apply to each member of the collection and typically have an object as a
191target e.g. update,delete) to be enabled by default. By default, this is only
192'view'
193
194=over 4
195
196=item B<_build_defualt_member_actions - Provided builder method, see METHODS
197
198=item B<has_default_member_actions> - Auto generated predicate
199
200=item B<clear_default_member_actions>- Auto generated clearer method
201
202=back
203
204=head2 default_collection_actions
205
206Read-write lazy building arrayref. The names of the collection actions (the
207actions that apply to the entire collection and typically have a collection as
208a target e.g. create, delete_all) to be enabled by default. By default, this
209is only empty.
210
211=over 4
212
213=item B<_build_defualt_member_actions - Provided builder method, see METHODS
214
215=item B<has_default_member_actions> - Auto generated predicate
216
217=item B<clear_default_member_actions>- Auto generated clearer method
218
219=back
220
b3832dbc 221=head1 METHODS
222
223=head2 get_collection $c
224
225Returns an instance of the collection this controller uses.
226
227=head2 _build_action_viewport_map
228
b3cb974a 229Provided builder for C<action_viewport_map>. Returns a hash containing:
b3832dbc 230
b3cb974a 231 list => 'Reaction::UI::ViewPort::Collection::Grid',
b3832dbc 232 view => 'Reaction::UI::ViewPort::Object',
233
234=head2 _build_action_viewport_args
235
b3cb974a 236By default will reurn a hashref containing action prototypes for all default
237member and collection actions. The prototype URI generators are generated by
238C<_build_member_action_prototype> and C<_build_collection_action_prototype>
239respectively and labels are the result of replacing underscores in the name
240with spaces and capitalizing the first letter. If you plan to use custom
241actions that are not supported by this scheme or you would like to customize
242the values it is suggested you wrap / override this method.
243
244Default output for a controller having only 'view' enabled:
245
246 { list => {
247 action_prototypes => {},
248 Member => {
249 action_prototypes => {
250 view => {label => 'View', uri => sub{...} },
251 },
252 },
253 },
254 }
255
256=head2 _build_member_action_prototype $label, $action_name
257
258Creates an action prototype suitable for creating action links in
259L<Reaction::UI::ViewPort::Role::Actions>. C<$action_name> should be the name of
260a Catalyst action in this controller.The prototype will generate a URI
261based on the action, current captures.
262
263=head2 _build_collection_action_prototype $label, $action_name
b3832dbc 264
7d3fe0d2 265=head2 basic_page $c, \%vp_args
266
267Accepts two arguments, context, and a hashref of viewport arguments. It will
268automatically determine the action name using the catalyst stack and call
0ccdac9b 269C<push_viewport> with the ViewPort class name contained in the
7d3fe0d2 270C<action_viewport_map> with a set of options determined by merging C<$vp_args>
271and the arguments contained in C<action_viewport_args>, if any.
272
b3832dbc 273=head1 ACTIONS
274
275=head2 base
276
277Chain link, no-op.
278
279=head2 list
280
7d3fe0d2 281Chain link, chained to C<base>. C<list> fetches the collection for the model
282and calls C<basic_page> with a single argument, C<collection>.
b3832dbc 283
7d3fe0d2 284The default ViewPort for this action is C<Reaction::UI::ViewPort::ListView> and
285can be changed by altering the C<action_viewport_map> attribute hash.
b3832dbc 286
287=head2 object
288
0ccdac9b 289Chain link, chained to C<base>, captures one argument, 'id'. Attempts to find
7d3fe0d2 290a single object by searching for a member of the current collection which has a
291Primary Key or Unique constraint matching that argument. If the object is found
292it is stored in the stash under the C<object> key.
b3832dbc 293
294=head2 view
295
7d3fe0d2 296Chain link, chained to C<object>. Calls C<basic page> with one argument,
297C<model>, which contains an instance of the object fetched by the C<object>
298action link.
b3832dbc 299
0ccdac9b 300The default ViewPort for this action is C<Reaction::UI::ViewPort::Object> and
7d3fe0d2 301can be changed by altering the C<action_viewport_map> attribute hash.
b3832dbc 302
de5b3fab 303=head1 SEE ALSO
b3832dbc 304
7d3fe0d2 305L<Reaction::UI::Controller>
b3832dbc 306
307=head1 AUTHORS
308
309See L<Reaction::Class> for authors.
310
311=head1 LICENSE
312
313See L<Reaction::Class> for the license.
314
315=cut