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 | |
a4f82080 |
12 | has 'model_name' => (isa => 'Str', is => 'rw', required => 1); |
13 | has 'collection_name' => (isa => 'Str', is => 'rw', required => 1); |
7adfd53f |
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 | |
89939ff9 |
18 | sub _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 |
29 | sub _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 | |
52 | sub base :Action :CaptureArgs(0) { |
53 | my ($self, $c) = @_; |
54 | } |
55 | |
643c09b7 |
56 | #XXX candidate for futre optimization, should cache reader? |
7adfd53f |
57 | sub 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 | |
65 | sub 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 |
81 | sub list :Chained('base') :PathPart('') :Args(0) { |
82 | my ($self, $c) = @_; |
643c09b7 |
83 | $c->forward(basic_page => { collection => $self->get_collection($c) }); |
7adfd53f |
84 | } |
85 | |
86 | sub 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 | |
95 | sub 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 | |
100 | sub 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 |
108 | sub 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 | |
114 | sub 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 | |
123 | sub 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 | |
132 | sub 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 | |
141 | sub 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 | |
158 | sub 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 | |
169 | 1; |