argh! missed DEBUG_EVENTS
[catagits/Reaction.git] / lib / Reaction / UI / ViewPort / Action.pm
1 package Reaction::UI::ViewPort::Action;
2
3 use Reaction::Class;
4
5 use MooseX::Types::URI qw/Uri/;
6 use MooseX::Types::Moose qw/Int Str/;
7 use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
8
9 use namespace::clean -except => [ qw(meta) ];
10
11 extends 'Reaction::UI::ViewPort::Object::Mutable';
12 with 'Reaction::UI::ViewPort::Action::Role::OK';
13
14 sub DEBUG_EVENTS () { $ENV{REACTION_UI_VIEWPORT_DEBUG_EVENTS} }
15
16 has message => (is => 'rw', isa => Str);
17 has '+model' => (handles => [qw/error_message has_error_message/]);
18
19 #this has to fucking go. it BLOWS.
20 has method => (
21   is => 'rw',
22   isa => NonEmptySimpleStr,
23   default => sub { 'post' }
24 );
25
26 has action => ( is => 'rw', isa => Uri );
27
28 has changed => (
29   is => 'rw',
30   isa => Int,
31   reader => 'is_changed',
32   default => sub{0}
33 );
34
35 sub can_apply {
36   my ($self) = @_;
37   foreach my $field ( @{ $self->fields } ) {
38     return 0 if $field->needs_sync;
39     # if e.g. a datetime field has an invalid value that can't be re-assembled
40     # into a datetime object, the action may be in a consistent state but
41     # not synchronized from the fields; in this case, we must not apply
42   }
43   return $self->model->can_apply;
44 }
45
46 sub do_apply {
47   shift->model->do_apply;
48 }
49
50 after apply_child_events => sub {
51   # interrupt here because fields will have been updated
52   my ($self) = @_;
53   $self->sync_action_from_fields;
54 };
55
56 sub sync_action_from_fields {
57   my ($self) = @_;
58   foreach my $field (@{$self->fields}) {
59     $field->sync_to_action; # get the field to populate the $action if possible
60   }
61   $self->model->sync_all;
62   foreach my $field (@{$self->fields}) {
63     $field->sync_from_action; # get errors from $action if applicable
64   }
65 }
66
67 after handle_events => sub {
68   my ($self, $events) = @_;
69   foreach my $event ($self->accept_events) {
70     unless (exists $events->{$event} ) {
71       # for <input type="image"... buttons
72       if ( exists $events->{"${event}.x"} && exists $events->{"${event}.y"} ) {
73         $self->_dump_event($event, $events->{$event}) if DEBUG_EVENTS;
74         $self->$event($events->{$event});
75       }
76     }
77   }
78 };
79
80 __PACKAGE__->meta->make_immutable;
81
82 1;
83
84 __END__;
85
86 =head1 NAME
87
88 Reaction::UI::ViewPort::Action - Provide user with a form with OK, Apply and Close.
89
90 =head1 SYNOPSIS
91
92   $controller->push_viewport('Reaction::UI::ViewPort::Action',
93     model           => $interface_model_action,
94     field_order     => [qw( firstname lastname )],
95     excluded_fields => [qw( password )],
96   );
97
98 =head1 DESCRIPTION
99
100 This subclass of L<Reaction::UI::ViewPort::Object::Mutable> is used for 
101 rendering a complete form supporting Apply, Close and OK.
102
103 =head1 ATTRIBUTES
104
105 =head2 message
106
107 =head2 model
108
109 Inherited from L<Reaction::UI::ViewPort::Object::Mutable>. Must be a
110 L<Reaction::InterfaceModel::Action>.
111
112 Also handles C<error_message> and C<has_error_message> methods.
113
114 =head2 method
115
116 post / get
117
118 =head2 changed
119
120 Returns true if a field has been edited.
121
122 =head1 METHODS
123
124 =head2 can_apply
125
126 Returns true if no field C<needs_sync> and the L</model> C<can_apply>.
127
128 =head2 do_apply
129
130 Delegates to C<do_apply> on the L</model>, which is a 
131 L<Reaction::InterfaceModel::Action>.
132
133 =head2 sync_action_from_fields
134
135 Firstly calls C<sync_to_action> on every L<Reaction::UI::ViewPort::Field::Mutable>
136 in L<fields|Reaction::UI::ViewPort::Object/fields>. Then it calls C<sync_all> on
137 the L<Reaction::InterfaceModel::Action> in L</model>. Next it will call
138 C<sync_from_action> on every field to repopulate them from the L</model>.
139
140 =head1 SUBCLASSING
141
142   package MyApp::UI::ViewPort::Action;
143   use Reaction::Class;
144   use MooseX::Types::Moose qw( Int );
145
146   use namespace::clean -except => 'meta';
147
148   extends 'Reaction::UI::ViewPort::Action';
149
150   has render_timestamp => (
151     is       => 'ro',
152     isa      => Int,
153     default  => sub { time },
154     required => 1,
155   );
156
157   has '+field_order' => (default => sub {[qw( firstname lastname )]});
158
159   1;
160
161 =head1 SEE ALSO
162
163 L<Reaction::UI::ViewPort>
164
165 L<Reaction::UI::ViewPort::Object>
166
167 L<Reaction::UI::ViewPort::Object::Mutable>
168
169 L<Reaction::InterfaceModel::Action::Role::Apply>
170
171 L<Reaction::InterfaceModel::Action::Role::Close>
172
173 L<Reaction::InterfaceModel::Action::Role::OK>
174
175 =head1 AUTHORS
176
177 See L<Reaction::Class> for authors.
178
179 =head1 LICENSE
180
181 See L<Reaction::Class> for the license.
182
183 =cut
184