oops, missing comma
[catagits/Reaction.git] / lib / Reaction / UI / CRUDController.pm
CommitLineData
7adfd53f 1package Reaction::UI::CRUDController;
2
3use strict;
4use warnings;
5use base 'Reaction::UI::Controller';
6use Reaction::Class;
7
8use aliased 'Reaction::UI::ViewPort::ListView';
9use aliased 'Reaction::UI::ViewPort::ActionForm';
10use aliased 'Reaction::UI::ViewPort::ObjectView';
11
a4f82080 12has 'model_name' => (isa => 'Str', is => 'rw', required => 1);
13has 'collection_name' => (isa => 'Str', is => 'rw', required => 1);
7adfd53f 14
b8faba69 15has action_viewport_map => (isa => 'HashRef', is => 'rw', lazy_build => 1);
16has action_viewport_args => (isa => 'HashRef', is => 'rw', lazy_build => 1);
17
89939ff9 18sub _build_action_viewport_map {
b8faba69 19 return {
20 list => ListView,
21 view => ObjectView,
22 create => ActionForm,
23 update => ActionForm,
24 delete => ActionForm,
25 delete_all => ActionForm,
26 };
27}
28
89939ff9 29sub _build_action_viewport_args {
b8faba69 30 my $self = shift;
31 return { list =>
32 { action_prototypes =>
33 [ { label => 'Create', action => sub {
34 [ '', 'create', $_[1]->req->captures ] } },
35 { label => 'Delete all', action => sub {
36 [ '', 'delete_all', $_[1]->req->captures ] } },
37 ],
38 Entity =>
39 { action_prototypes =>
40 [ { label => 'View', action => sub {
41 [ '', 'view', [ @{$_[1]->req->captures}, $_[0]->__id ] ] } },
42 { label => 'Edit', action => sub {
43 [ '', 'update', [ @{$_[1]->req->captures}, $_[0]->__id ] ] } },
44 { label => 'Delete', action => sub {
45 [ '', 'delete', [ @{$_[1]->req->captures}, $_[0]->__id ] ] } },
46 ],
47 },
48 },
49 };
50}
7adfd53f 51
52sub base :Action :CaptureArgs(0) {
53 my ($self, $c) = @_;
54}
55
643c09b7 56#XXX candidate for futre optimization, should cache reader?
7adfd53f 57sub get_collection {
58 my ($self, $c) = @_;
a4f82080 59 my $model = $c->model( $self->model_name );
60 my $attr = $model->meta->find_attribute_by_name( $self->collection_name );
61 my $reader = $attr->get_read_method;
62 return $model->$reader;
7adfd53f 63}
64
65sub get_model_action {
66 my ($self, $c, $name, $target) = @_;
67
68 if ($target->can('action_for')) {
69 return $target->action_for($name, ctx => $c);
70 }
71
f670cfd0 72 #can we please kill this already?
7adfd53f 73 my $model_name = "Action::${name}".$self->model_name;
74 my $model = $c->model($model_name);
75 confess "no such Model $model_name" unless $model;
76 return $model->new(target_model => $target, ctx => $c);
77}
78
643c09b7 79
80
7adfd53f 81sub list :Chained('base') :PathPart('') :Args(0) {
82 my ($self, $c) = @_;
643c09b7 83 $c->forward(basic_page => { collection => $self->get_collection($c) });
7adfd53f 84}
85
86sub create :Chained('base') :PathPart('create') :Args(0) {
87 my ($self, $c) = @_;
643c09b7 88 my $vp_args = {
59836a9d 89 next_action => 'list',
643c09b7 90 on_apply_callback => sub { $self->after_create_callback($c => @_); },
91 };
92 $c->forward( basic_model_action => $vp_args);
b8faba69 93}
94
95sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) {
96 my ($self, $c) = @_;
643c09b7 97 $c->forward(basic_model_action => { next_action => 'list'});
7adfd53f 98}
99
100sub after_create_callback {
101 my ($self, $c, $vp, $result) = @_;
b8faba69 102 return $self->redirect_to
103 ( $c, 'update', [ @{$c->req->captures}, $result->id ] );
7adfd53f 104}
105
643c09b7 106
107
7adfd53f 108sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
109 my ($self, $c, $key) = @_;
f670cfd0 110 my $object :Stashed = $self->get_collection($c)->find($key);
7adfd53f 111 confess "Object? what object?" unless $object; # should be a 404.
112}
113
114sub update :Chained('object') :Args(0) {
115 my ($self, $c) = @_;
59836a9d 116 #this needs a better solution. currently thinking about it
7adfd53f 117 my @cap = @{$c->req->captures};
118 pop(@cap); # object id
643c09b7 119 my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
120 $c->forward(basic_model_action => $vp_args);
7adfd53f 121}
122
123sub delete :Chained('object') :Args(0) {
124 my ($self, $c) = @_;
59836a9d 125 #this needs a better solution. currently thinking about it
7adfd53f 126 my @cap = @{$c->req->captures};
127 pop(@cap); # object id
643c09b7 128 my $vp_args = { next_action => [ $self, 'redirect_to', 'list', \@cap ]};
129 $c->forward(basic_model_action => $vp_args);
7adfd53f 130}
131
132sub view :Chained('object') :Args(0) {
133 my ($self, $c) = @_;
134 my $object :Stashed;
643c09b7 135 $c->forward(basic_page => {object => $object});
136}
137
138
139
140
141sub basic_model_action :Private {
142 my ($self, $c, $vp_args) = @_;
143
144 my $target = exists $c->stash->{object} ?
145 $c->stash->{object} : $self->get_collection($c);
146
147 my $cat_action_name = $c->stack->[-2]->name;
148 my $im_action_name = join('', (map{ uc } split('_', $cat_action_name)));
149 return $self->push_viewport
150 (
151 $self->action_viewport_map->{$cat_action_name},
152 action => $self->get_model_action($c, $im_action_name, $target),
153 %{ $vp_args || {} },
154 %{ $self->action_viewport_args->{$cat_action_name} || {} },
155 );
156}
157
158sub basic_page : Private {
159 my ($self, $c, $vp_args) = @_;
160 my $action_name = $c->stack->[-2]->name;
161 return $self->push_viewport
b8faba69 162 (
643c09b7 163 $self->action_viewport_map->{$action_name},
164 %{ $vp_args || {} },
165 %{ $self->action_viewport_args->{$action_name} || {} },
b8faba69 166 );
7adfd53f 167}
168
1691;