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