Commit | Line | Data |
89b70ba7 |
1 | package Reaction::UI::Controller::Collection; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | use base 'Reaction::UI::Controller'; |
6 | use Reaction::Class; |
7 | |
8cc0103d |
8 | use aliased 'Reaction::UI::ViewPort::Collection::Grid'; |
c8fbb8ad |
9 | use aliased 'Reaction::UI::ViewPort::Object'; |
89b70ba7 |
10 | |
37728bba |
11 | has model_name => (isa => 'Str', is => 'rw', required => 1); |
12 | has collection_name => (isa => 'Str', is => 'rw', required => 1); |
89b70ba7 |
13 | |
37728bba |
14 | has action_viewport_map => (isa => 'HashRef', is => 'rw', lazy_build => 1); |
89b70ba7 |
15 | has action_viewport_args => (isa => 'HashRef', is => 'rw', lazy_build => 1); |
16 | |
37728bba |
17 | has default_member_actions => ( |
18 | isa => 'ArrayRef', |
19 | is => 'rw', |
20 | lazy_build => 1 |
21 | ); |
22 | |
23 | has default_collection_actions => ( |
24 | isa => 'ArrayRef', |
25 | is => 'rw', |
26 | lazy_build => 1 |
27 | ); |
28 | |
29 | sub _build_default_member_actions { ['view'] } |
30 | |
31 | sub _build_default_collection_actions { [] } |
32 | |
89b70ba7 |
33 | sub _build_action_viewport_map { |
37728bba |
34 | my $self = shift; |
35 | my %map; |
36 | $map{list} = Grid; |
37 | $map{view} = Object if grep {$_ eq 'view'} @{$self->default_member_actions}; |
38 | return \%map; |
89b70ba7 |
39 | } |
40 | |
41 | sub _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 | |
62 | sub _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 | |
73 | sub _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? |
85 | sub 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 |
98 | sub base :Action :CaptureArgs(0) { |
89b70ba7 |
99 | my ($self, $c) = @_; |
89b70ba7 |
100 | } |
101 | |
102 | sub 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 |
109 | sub 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 |
114 | sub view :Chained('object') :Args(0) { |
115 | my ($self, $c) = @_; |
b68d6a35 |
116 | $self->basic_page($c, { model => $c->stash->{object} }); |
89b70ba7 |
117 | } |
118 | |
aee256be |
119 | sub 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 | |
131 | 1; |
b3832dbc |
132 | |
b3832dbc |
133 | __END__; |
134 | |
135 | =head1 NAME |
136 | |
7d3fe0d2 |
137 | Reaction::UI::Controller |
b3832dbc |
138 | |
139 | =head1 DESCRIPTION |
140 | |
141 | Controller class used to make displaying collections easier. |
142 | Inherits from L<Reaction::UI::Controller>. |
143 | |
144 | =head1 ATTRIBUTES |
145 | |
146 | =head2 model_name |
147 | |
0ccdac9b |
148 | The name of the model this controller will use as it's data source. Should be a |
7d3fe0d2 |
149 | name that can be passed to C<$C-E<gt>model> |
b3832dbc |
150 | |
151 | =head2 collection_name |
152 | |
0ccdac9b |
153 | The name of the collection whithin the model that this Controller will be |
7d3fe0d2 |
154 | utilizing. |
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 |
168 | Read-write lazy building hashref. The keys should match action names in the |
169 | Controller and the value should be the ViewPort class that this action should |
7d3fe0d2 |
170 | use. See method C<basic_page> for more info. |
b3832dbc |
171 | |
de5b3fab |
172 | =head2 action_viewport_args |
b3832dbc |
173 | |
0ccdac9b |
174 | Read-write lazy building hashref. Additional ViewPort arguments for the action |
7d3fe0d2 |
175 | named 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 | |
187 | =head1 METHODS |
188 | |
189 | =head2 get_collection $c |
190 | |
191 | Returns an instance of the collection this controller uses. |
192 | |
193 | =head2 _build_action_viewport_map |
194 | |
195 | Provided builder for C<action_viewport_map>. Returns a hash with two items: |
196 | |
197 | list => 'Reaction::UI::ViewPort::ListView', |
198 | view => 'Reaction::UI::ViewPort::Object', |
199 | |
200 | =head2 _build_action_viewport_args |
201 | |
202 | Returns an empty hashref. |
203 | |
7d3fe0d2 |
204 | =head2 basic_page $c, \%vp_args |
205 | |
206 | Accepts two arguments, context, and a hashref of viewport arguments. It will |
207 | automatically determine the action name using the catalyst stack and call |
0ccdac9b |
208 | C<push_viewport> with the ViewPort class name contained in the |
7d3fe0d2 |
209 | C<action_viewport_map> with a set of options determined by merging C<$vp_args> |
210 | and the arguments contained in C<action_viewport_args>, if any. |
211 | |
b3832dbc |
212 | =head1 ACTIONS |
213 | |
214 | =head2 base |
215 | |
216 | Chain link, no-op. |
217 | |
218 | =head2 list |
219 | |
7d3fe0d2 |
220 | Chain link, chained to C<base>. C<list> fetches the collection for the model |
221 | and calls C<basic_page> with a single argument, C<collection>. |
b3832dbc |
222 | |
7d3fe0d2 |
223 | The default ViewPort for this action is C<Reaction::UI::ViewPort::ListView> and |
224 | can be changed by altering the C<action_viewport_map> attribute hash. |
b3832dbc |
225 | |
226 | =head2 object |
227 | |
0ccdac9b |
228 | Chain link, chained to C<base>, captures one argument, 'id'. Attempts to find |
7d3fe0d2 |
229 | a single object by searching for a member of the current collection which has a |
230 | Primary Key or Unique constraint matching that argument. If the object is found |
231 | it is stored in the stash under the C<object> key. |
b3832dbc |
232 | |
233 | =head2 view |
234 | |
7d3fe0d2 |
235 | Chain link, chained to C<object>. Calls C<basic page> with one argument, |
236 | C<model>, which contains an instance of the object fetched by the C<object> |
237 | action link. |
b3832dbc |
238 | |
0ccdac9b |
239 | The default ViewPort for this action is C<Reaction::UI::ViewPort::Object> and |
7d3fe0d2 |
240 | can be changed by altering the C<action_viewport_map> attribute hash. |
b3832dbc |
241 | |
de5b3fab |
242 | =head1 SEE ALSO |
b3832dbc |
243 | |
7d3fe0d2 |
244 | L<Reaction::UI::Controller> |
b3832dbc |
245 | |
246 | =head1 AUTHORS |
247 | |
248 | See L<Reaction::Class> for authors. |
249 | |
250 | =head1 LICENSE |
251 | |
252 | See L<Reaction::Class> for the license. |
253 | |
254 | =cut |