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