1 package Reaction::UI::CRUDController;
5 use base 'Reaction::UI::Controller';
8 use aliased 'Reaction::UI::ViewPort::ListView';
9 use aliased 'Reaction::UI::ViewPort::ActionForm';
10 use aliased 'Reaction::UI::ViewPort::ObjectView';
12 has 'model_name' => (isa => 'Str', is => 'rw', required => 1);
13 has 'collection_name' => (isa => 'Str', is => 'rw', required => 1);
15 has action_viewport_map => (isa => 'HashRef', is => 'rw', lazy_build => 1);
16 has action_viewport_args => (isa => 'HashRef', is => 'rw', lazy_build => 1);
18 sub build_action_viewport_map {
25 delete_all => ActionForm,
29 sub build_action_viewport_args {
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 ] } },
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 ] ] } },
52 sub base :Action :CaptureArgs(0) {
56 #XXX candidate for futre optimization
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;
65 sub get_model_action {
66 my ($self, $c, $name, $target) = @_;
68 if ($target->can('action_for')) {
69 return $target->action_for($name, ctx => $c);
72 #can we please kill this already?
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);
79 sub list :Chained('base') :PathPart('') :Args(0) {
83 $self->action_viewport_map->{list},
84 %{ $self->action_viewport_args->{list} || {} },
85 collection => $self->get_collection($c)
89 sub create :Chained('base') :PathPart('create') :Args(0) {
91 my $action = $self->get_model_action($c, 'Create', $self->get_collection($c));
94 $self->action_viewport_map->{create},
95 %{ $self->action_viewport_args->{create} || {} },
97 next_action => 'list',
98 on_apply_callback => sub { $self->after_create_callback($c => @_); },
102 sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) {
104 my $action = $self->get_model_action($c, 'DeleteAll', $self->get_collection($c));
107 $self->action_viewport_map->{delete_all},
108 %{ $self->action_viewport_args->{delete_all} || {} },
110 next_action => 'list',
114 sub after_create_callback {
115 my ($self, $c, $vp, $result) = @_;
116 return $self->redirect_to
117 ( $c, 'update', [ @{$c->req->captures}, $result->id ] );
120 sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
121 my ($self, $c, $key) = @_;
122 my $object :Stashed = $self->get_collection($c)->find($key);
123 confess "Object? what object?" unless $object; # should be a 404.
126 sub update :Chained('object') :Args(0) {
129 my $action = $self->get_model_action($c, 'Update', $object);
130 my @cap = @{$c->req->captures};
131 pop(@cap); # object id
134 $self->action_viewport_map->{update},
135 %{ $self->action_viewport_args->{update} || {} },
137 next_action => [ $self, 'redirect_to', 'list', \@cap ]
141 sub delete :Chained('object') :Args(0) {
144 my $action = $self->get_model_action($c, 'Delete', $object);
145 my @cap = @{$c->req->captures};
146 pop(@cap); # object id
149 $self->action_viewport_map->{delete},
150 %{ $self->action_viewport_args->{delete} || {} },
152 next_action => [ $self, 'redirect_to', 'list', \@cap ]
156 sub view :Chained('object') :Args(0) {
159 my @cap = @{$c->req->captures};
160 pop(@cap); # object id
163 $self->action_viewport_map->{view},
164 %{ $self->action_viewport_args->{view} || {} },