Commit | Line | Data |
7adfd53f |
1 | package Reaction::UI::CRUDController; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | use base 'Reaction::UI::Controller'; |
6 | use Reaction::Class; |
7 | |
8 | use aliased 'Reaction::UI::ViewPort::ListView'; |
9 | use aliased 'Reaction::UI::ViewPort::ActionForm'; |
10 | use aliased 'Reaction::UI::ViewPort::ObjectView'; |
11 | |
12 | has 'model_base' => (isa => 'Str', is => 'rw', required => 1); |
13 | has 'model_name' => (isa => 'Str', is => 'rw', required => 1); |
14 | |
b8faba69 |
15 | has action_viewport_map => (isa => 'HashRef', is => 'rw', lazy_build => 1); |
16 | has action_viewport_args => (isa => 'HashRef', is => 'rw', lazy_build => 1); |
17 | |
18 | sub build_action_viewport_map { |
19 | return { |
20 | list => ListView, |
21 | view => ObjectView, |
22 | create => ActionForm, |
23 | update => ActionForm, |
24 | delete => ActionForm, |
25 | delete_all => ActionForm, |
26 | }; |
27 | } |
28 | |
29 | sub build_action_viewport_args { |
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 | |
52 | sub base :Action :CaptureArgs(0) { |
53 | my ($self, $c) = @_; |
54 | } |
55 | |
56 | sub get_collection { |
57 | my ($self, $c) = @_; |
f670cfd0 |
58 | #this sucks and should be fixed |
7adfd53f |
59 | return $c->model(join('::', $self->model_base, $self->model_name)); |
60 | } |
61 | |
62 | sub get_model_action { |
63 | my ($self, $c, $name, $target) = @_; |
64 | |
65 | if ($target->can('action_for')) { |
66 | return $target->action_for($name, ctx => $c); |
67 | } |
68 | |
f670cfd0 |
69 | #can we please kill this already? |
7adfd53f |
70 | my $model_name = "Action::${name}".$self->model_name; |
71 | my $model = $c->model($model_name); |
72 | confess "no such Model $model_name" unless $model; |
73 | return $model->new(target_model => $target, ctx => $c); |
74 | } |
75 | |
76 | sub list :Chained('base') :PathPart('') :Args(0) { |
77 | my ($self, $c) = @_; |
78 | |
79 | $self->push_viewport( |
b8faba69 |
80 | $self->action_viewport_map->{list}, |
81 | %{ $self->action_viewport_args->{list} || {} }, |
82 | collection => $self->get_collection($c) |
83 | ); |
7adfd53f |
84 | } |
85 | |
86 | sub create :Chained('base') :PathPart('create') :Args(0) { |
87 | my ($self, $c) = @_; |
88 | my $action = $self->get_model_action($c, 'Create', $self->get_collection($c)); |
b8faba69 |
89 | $self->push_viewport |
90 | ( |
91 | $self->action_viewport_map->{create}, |
92 | %{ $self->action_viewport_args->{create} || {} }, |
93 | action => $action, |
94 | next_action => 'list', |
95 | on_apply_callback => sub { $self->after_create_callback($c => @_); }, |
96 | ); |
97 | } |
98 | |
99 | sub delete_all :Chained('base') :PathPart('delete_all') :Args(0) { |
100 | my ($self, $c) = @_; |
101 | my $action = $self->get_model_action($c, 'DeleteAll', $self->get_collection($c)); |
102 | $self->push_viewport |
103 | ( |
104 | $self->action_viewport_map->{delete_all}, |
105 | %{ $self->action_viewport_args->{delete_all} || {} }, |
106 | action => $action, |
107 | next_action => 'list', |
108 | ); |
7adfd53f |
109 | } |
110 | |
111 | sub after_create_callback { |
112 | my ($self, $c, $vp, $result) = @_; |
b8faba69 |
113 | return $self->redirect_to |
114 | ( $c, 'update', [ @{$c->req->captures}, $result->id ] ); |
7adfd53f |
115 | } |
116 | |
117 | sub object :Chained('base') :PathPart('id') :CaptureArgs(1) { |
118 | my ($self, $c, $key) = @_; |
f670cfd0 |
119 | my $object :Stashed = $self->get_collection($c)->find($key); |
7adfd53f |
120 | confess "Object? what object?" unless $object; # should be a 404. |
121 | } |
122 | |
123 | sub update :Chained('object') :Args(0) { |
124 | my ($self, $c) = @_; |
125 | my $object :Stashed; |
126 | my $action = $self->get_model_action($c, 'Update', $object); |
127 | my @cap = @{$c->req->captures}; |
128 | pop(@cap); # object id |
b8faba69 |
129 | $self->push_viewport |
130 | ( |
131 | $self->action_viewport_map->{update}, |
132 | %{ $self->action_viewport_args->{update} || {} }, |
133 | action => $action, |
134 | next_action => [ $self, 'redirect_to', 'list', \@cap ] |
7adfd53f |
135 | ); |
136 | } |
137 | |
138 | sub delete :Chained('object') :Args(0) { |
139 | my ($self, $c) = @_; |
140 | my $object :Stashed; |
141 | my $action = $self->get_model_action($c, 'Delete', $object); |
142 | my @cap = @{$c->req->captures}; |
143 | pop(@cap); # object id |
b8faba69 |
144 | $self->push_viewport |
145 | ( |
146 | $self->action_viewport_map->{delete}, |
147 | %{ $self->action_viewport_args->{delete} || {} }, |
148 | action => $action, |
149 | next_action => [ $self, 'redirect_to', 'list', \@cap ] |
7adfd53f |
150 | ); |
151 | } |
152 | |
153 | sub view :Chained('object') :Args(0) { |
154 | my ($self, $c) = @_; |
155 | my $object :Stashed; |
156 | my @cap = @{$c->req->captures}; |
157 | pop(@cap); # object id |
b8faba69 |
158 | $self->push_viewport |
159 | ( |
160 | $self->action_viewport_map->{view}, |
161 | %{ $self->action_viewport_args->{view} || {} }, |
162 | object => $object, |
163 | ); |
7adfd53f |
164 | } |
165 | |
166 | 1; |