eac550dccbe1f37733df1d697314f33d2f8c53e8
[catagits/Reaction.git] / lib / Reaction / UI / Controller.pm
1 package Reaction::UI::Controller;
2
3 use base qw(
4   Catalyst::Controller
5   Catalyst::Component::ACCEPT_CONTEXT
6   Reaction::Object
7 );
8
9 use Reaction::Class;
10 use Scalar::Util 'weaken';
11 use namespace::clean -except => [ qw(meta) ];
12
13 sub push_viewport {
14   my $self = shift;
15   my $c = $self->context;
16   my $focus_stack = $c->stash->{focus_stack};
17   my ($class, @proto_args) = @_;
18   my %args;
19   if (my $vp_attr = $c->stack->[-1]->attributes->{ViewPort}) {
20     if (ref($vp_attr) eq 'ARRAY') {
21       $vp_attr = $vp_attr->[0];
22     }
23     if (ref($vp_attr) eq 'HASH') {
24       if (my $conf_class = delete $vp_attr->{class}) {
25         $class = $conf_class;
26       }
27       %args = %{ $self->merge_config_hashes($vp_attr, {@proto_args}) };
28     } else {
29       $class = $vp_attr;
30       %args = @proto_args;
31     }
32   } else {
33     %args = @proto_args;
34   }
35
36   $args{ctx} = $c;
37
38   if (exists $args{next_action} && !ref($args{next_action})) {
39     $args{next_action} = [ $self, 'redirect_to', $args{next_action} ];
40   }
41   $focus_stack->push_viewport($class, %args);
42 }
43
44 sub pop_viewport {
45   return shift->context->stash->{focus_stack}->pop_viewport;
46 }
47
48 sub pop_viewports_to {
49   my ($self, $vp) = @_;
50   return $self->context->stash->{focus_stack}->pop_viewports_to($vp);
51 }
52
53 sub redirect_to {
54   my ($self, $c, $to, $cap, $args, $attrs) = @_;
55
56   #the confess calls could be changed later to $c->log ?
57   my $action;
58   my $reftype = ref($to);
59   if( $reftype eq '' ){
60     $action = $self->action_for($to);
61     confess("Failed to locate action ${to} in " . blessed($self)) unless $action;
62   } elsif($reftype eq 'ARRAY' && @$to == 2){ #is that overkill / too strict?
63     $action = $c->controller($to->[0])->action_for($to->[1]);
64     confess("Failed to locate action $to->[1] in $to->[0]" ) unless $action;
65   } elsif( blessed $to && $to->isa('Catalyst::Action') ){
66     $action = $to;
67   } else{
68     confess("Failed to locate action from ${to}");
69   }
70
71   $cap ||= $c->req->captures;
72   $args ||= $c->req->args;
73   $attrs ||= {};
74   my $uri = $c->uri_for($action, $cap, @$args, $attrs);
75   $c->res->redirect($uri);
76 }
77
78 sub make_context_closure {
79   my($self, $closure) = @_;
80   my $ctx = $self->context;
81   weaken($ctx);
82   return sub { $closure->($ctx, @_) };
83 }
84
85 1;
86
87 __END__;
88
89 =head1 NAME
90
91 Reaction::UI::Controller
92
93 =head1 DESCRIPTION
94
95 Base Reaction Controller class. Inherits from:
96
97 =over 4
98
99 =item L<Catalyst::Controller>
100 =item L<Catalyst::Component::ACCEPT_CONTEXT>
101 =item L<Reaction::Object>
102
103 =back
104
105 =head1 METHODS
106
107 =head2 push_viewport $vp_class, %args
108
109 Creates a new instance of the L<Reaction::UI::ViewPort> class
110 ($vp_class) using the rest of the arguments given (%args). Defaults of
111 the action can be overridden by using the C<ViewPort> key in the
112 controller configuration. For example to override the default number
113 of items in a CRUD list action:
114
115 __PACKAGE__->config(
116                     action => { 
117                         list => { ViewPort => { per_page => 50 } },
118     }
119   );
120
121 The ViewPort is added to the L<Reaction::UI::Window>'s FocusStack in
122 the stash, and also returned to the calling code.
123
124 Related items:
125
126 =over
127
128 =item L<Reaction::UI::Controller::Root>
129 =item L<Reaction::UI::Window>
130
131 =back
132
133 TODO: explain how next_action as a scalar gets converted to the redirect arrayref thing
134
135 =head2 pop_viewport
136
137 =head2 pop_viewport_to $vp
138
139 Call L<Reaction::UI::FocusStack/pop_viewport> or
140 L<Reaction::UI::FocusStack/pop_viewport_to> on 
141 the C<< $c->stash->{focus_stack} >>.
142
143 =head2 redirect_to $c, $to, $captures, $args, $attrs
144
145 Construct a URI and redirect to it.
146
147 $to can be:
148
149 =over
150
151 =item The name of an action in the current controller.
152
153 =item A L<Catalyst::Action> instance.
154
155 =item An arrayref of controller name and the name of an action in that
156 controller.
157
158 =back
159
160 $captures and $args default to the current requests $captures and
161 $args if not supplied.
162
163 =head1 AUTHORS
164
165 See L<Reaction::Class> for authors.
166
167 =head1 LICENSE
168
169 See L<Reaction::Class> for the license.
170
171 =cut