fix for action_filter
[catagits/Reaction.git] / lib / Reaction / UI / Controller.pm
CommitLineData
7adfd53f 1package Reaction::UI::Controller;
2
7f43bb45 3use base qw(Catalyst::Controller); # Reaction::Object);
1810d302 4
7adfd53f 5use Reaction::Class;
747a1feb 6use Scalar::Util 'weaken';
db80cb74 7use namespace::clean -except => [ qw(meta) ];
7adfd53f 8
d0736579 9has context => (is => 'ro', isa => 'Object', weak_ref => 1);
10with 'Catalyst::Component::InstancePerContext';
11
12sub build_per_context_instance {
13 my ($self, $c, @args) = @_;
14 my $newself = $self->new($self->_application, {%$self, context => $c, @args});
15 return $newself;
16}
17
7adfd53f 18sub push_viewport {
19 my $self = shift;
1810d302 20 my $c = $self->context;
21 my $focus_stack = $c->stash->{focus_stack};
7adfd53f 22 my ($class, @proto_args) = @_;
23 my %args;
7adfd53f 24 if (my $vp_attr = $c->stack->[-1]->attributes->{ViewPort}) {
25 if (ref($vp_attr) eq 'ARRAY') {
26 $vp_attr = $vp_attr->[0];
27 }
28 if (ref($vp_attr) eq 'HASH') {
29 if (my $conf_class = delete $vp_attr->{class}) {
30 $class = $conf_class;
31 }
504e2c44 32 %args = %{ $self->merge_config_hashes($vp_attr, {@proto_args}) };
7adfd53f 33 } else {
34 $class = $vp_attr;
35 %args = @proto_args;
36 }
37 } else {
38 %args = @proto_args;
39 }
40
41 $args{ctx} = $c;
42
43 if (exists $args{next_action} && !ref($args{next_action})) {
44 $args{next_action} = [ $self, 'redirect_to', $args{next_action} ];
45 }
46 $focus_stack->push_viewport($class, %args);
47}
48
49sub pop_viewport {
1810d302 50 return shift->context->stash->{focus_stack}->pop_viewport;
7adfd53f 51}
52
53sub pop_viewports_to {
54 my ($self, $vp) = @_;
1810d302 55 return $self->context->stash->{focus_stack}->pop_viewports_to($vp);
7adfd53f 56}
57
58sub redirect_to {
59 my ($self, $c, $to, $cap, $args, $attrs) = @_;
60
61 #the confess calls could be changed later to $c->log ?
62 my $action;
9cf5bc0b 63 my $reftype = ref($to);
64 if( $reftype eq '' ){
65 $action = $self->action_for($to);
66 confess("Failed to locate action ${to} in " . blessed($self)) unless $action;
67 } elsif($reftype eq 'ARRAY' && @$to == 2){ #is that overkill / too strict?
68 $action = $c->controller($to->[0])->action_for($to->[1]);
69 confess("Failed to locate action $to->[1] in $to->[0]" ) unless $action;
70 } elsif( blessed $to && $to->isa('Catalyst::Action') ){
71 $action = $to;
7adfd53f 72 } else{
9cf5bc0b 73 confess("Failed to locate action from ${to}");
7adfd53f 74 }
75
76 $cap ||= $c->req->captures;
77 $args ||= $c->req->args;
78 $attrs ||= {};
79 my $uri = $c->uri_for($action, $cap, @$args, $attrs);
80 $c->res->redirect($uri);
81}
82
747a1feb 83sub make_context_closure {
84 my($self, $closure) = @_;
85 my $ctx = $self->context;
db80cb74 86 weaken($ctx);
747a1feb 87 return sub { $closure->($ctx, @_) };
88}
89
7adfd53f 901;
f2756356 91
92__END__;
93
94=head1 NAME
95
63bb30b4 96Reaction::UI::Controller - Reaction Base Controller Class
97
98=head1 SYNOPSIS
99
100 package MyApp::Controller::Foo;
101 use strict;
102 use warnings;
103 use parent 'Reaction::UI::Controller';
104
105 use aliased 'Reaction::UI::ViewPort';
106
107 sub foo: Chained('/base') Args(0) {
108 my ($self, $ctx) = @_;
109
110 $ctx->push_viewport(ViewPort,
111 layout => 'foo',
112 );
113 }
114
115 1;
f2756356 116
117=head1 DESCRIPTION
118
119Base Reaction Controller class. Inherits from:
120
121=over 4
122
123=item L<Catalyst::Controller>
124=item L<Catalyst::Component::ACCEPT_CONTEXT>
125=item L<Reaction::Object>
126
127=back
128
129=head1 METHODS
130
131=head2 push_viewport $vp_class, %args
132
f1cd5548 133Creates a new instance of the L<Reaction::UI::ViewPort> class
134($vp_class) using the rest of the arguments given (%args). Defaults of
135the action can be overridden by using the C<ViewPort> key in the
136controller configuration. For example to override the default number
137of items in a CRUD list action:
138
139__PACKAGE__->config(
140 action => {
141 list => { ViewPort => { per_page => 50 } },
142 }
143 );
144
145The ViewPort is added to the L<Reaction::UI::Window>'s FocusStack in
146the stash, and also returned to the calling code.
147
148Related items:
149
150=over
151
152=item L<Reaction::UI::Controller::Root>
153=item L<Reaction::UI::Window>
154
155=back
f2756356 156
157TODO: explain how next_action as a scalar gets converted to the redirect arrayref thing
158
159=head2 pop_viewport
160
161=head2 pop_viewport_to $vp
162
f1cd5548 163Call L<Reaction::UI::FocusStack/pop_viewport> or
164L<Reaction::UI::FocusStack/pop_viewport_to> on
165the C<< $c->stash->{focus_stack} >>.
f2756356 166
167=head2 redirect_to $c, $to, $captures, $args, $attrs
168
f1cd5548 169Construct a URI and redirect to it.
170
171$to can be:
172
173=over
f2756356 174
f1cd5548 175=item The name of an action in the current controller.
f2756356 176
f1cd5548 177=item A L<Catalyst::Action> instance.
178
179=item An arrayref of controller name and the name of an action in that
180controller.
181
182=back
f2756356 183
f1cd5548 184$captures and $args default to the current requests $captures and
185$args if not supplied.
f2756356 186
b4e081f8 187=head2 make_context_closure
188
189The purpose of this method is to prevent memory leaks.
190It weakens the context object, often denoted $c, and passes it as the
191first argument to the sub{} that is passed to the make_context_closure method.
192In other words,
193
194=over 4
195
196make_context_closure returns sub { $sub_you_gave_it->($weak_c, @_)
197
198=back
199
200To further expound up this useful construct consider code written before
201make_context_closure was created:
202
203 on_apply_callback =>
204 sub {
205 $self->after_search( $c, @_ );
206 }
207 ),
208
209This could be rewritten as:
210
211 on_apply_callback => $self->make_context_closure(
212 sub {
213 my $weak_c = shift;
214 $self->after_search( $weak_c, @_ );
215 }
216 ),
217
218Or even more succintly:
219
220 on_apply_callback => $self->make_context_closure(
221 sub {
222 $self->after_search( @_ );
223 }
224 ),
225
f2756356 226=head1 AUTHORS
227
228See L<Reaction::Class> for authors.
229
230=head1 LICENSE
231
232See L<Reaction::Class> for the license.
233
234=cut