1use warnings; 2use strict; 3 4package Jifty::Result; 5 6=head1 NAME 7 8Jifty::Result - Outcome of running a L<Jifty::Action> 9 10=head1 DESCRIPTION 11 12C<Jifty::Result> encapsulates the outcome of running a 13L<Jifty::Action>. Results are also stored on the framework's 14L<Jifty::Response> object. 15 16=cut 17 18 19 20use base qw/Jifty::Object Class::Accessor::Fast/; 21 22__PACKAGE__->mk_accessors(qw(failure action_class message _content)); 23 24 25=head2 new 26 27Construct a new action result. This is done automatically when the 28action is created, and can be accessed via the 29L<Jifty::Action/result>. 30 31=cut 32 33sub new { 34 my $class = shift; 35 my $self = bless {}, $class; 36 37 $self->failure(0); 38 $self->_content({}); 39 40 return $self; 41} 42 43=head2 failure [BOOL] 44 45Gets or sets if the action succeeded or failed. 46 47=head2 success [BOOL] 48 49Gets or sets if the action succeeded or failed -- this is an 50alternate interface from C<failure> but has the same effect. 51 52=cut 53 54sub success { 55 my $self = shift; 56 return 0 if $self->failure(map {not $_} @_); 57 return 1; 58} 59 60=head2 action_class [MESSAGE] 61 62Returns the class for the action that this result came from. 63 64=head2 message [MESSAGE] 65 66Gets or sets the action's response message. This is an informational 67textual description of the outcome of the action. 68 69=head2 error [ERROR] 70 71Gets or sets the action's error response. This is an informational 72textual description of what went wrong with the action, overall. This 73also automatically sets the result to be a L</failure>. 74 75=cut 76 77sub error { 78 my $self = shift; 79 80 $self->failure(1) if @_ and $_[0]; 81 $self->{error} = shift if @_; 82 return $self->{error}; 83} 84 85=head2 field_error FIELD [ERROR] [OPTIONS] 86 87Gets or sets the error string for a specific field on the action. 88This also automatically sets the result to be a failure. C<OPTIONS> 89is an optional set of key-value pairs; the only currently supported 90option is C<force>, which sets the L</ajax_force_validate> for this 91field. 92 93=cut 94 95sub field_error { 96 my $self = shift; 97 my $field = shift; 98 99 $self->failure(1) if @_ and $_[0]; 100 $self->{field_errors}{ $field } = shift if @_; 101 102 my %args = @_; 103 $self->{ajax_force_validate}{ $field } = $args{force} if exists $args{force}; 104 105 return $self->{field_errors}{ $field }; 106} 107 108=head2 field_errors 109 110Returns a hash which maps L<argument|Jifty::Manual::Glossary/argument> 111name to error. 112 113=cut 114 115sub field_errors { 116 my $self = shift; 117 return %{$self->{field_errors} || {}}; 118} 119 120=head2 field_warning FIELD [WARNING] [OPTIONS] 121 122Gets or sets the warning string for a specific field on the 123action. C<OPTIONS> is an optional set of key-value pairs; the only 124currently supported option is C<force>, which sets the 125L</ajax_force_validate> for this field. 126 127=cut 128 129sub field_warning { 130 my $self = shift; 131 my $field = shift; 132 133 $self->{field_warnings}{ $field } = shift if @_; 134 135 my %args = @_; 136 $self->{ajax_force_validate}{ $field } = $args{force} if exists $args{force}; 137 138 return $self->{field_warnings}{ $field }; 139} 140 141=head2 field_warnings 142 143Returns a hash which maps L<argument|Jifty::Manual::Glossary/argument> 144name to warning. 145 146=cut 147 148sub field_warnings { 149 my $self = shift; 150 return %{$self->{field_warnings} || {}}; 151} 152 153=head2 ajax_force_validate FIELD [VALUE] 154 155Gets or sets the flag which determines if warnings and errors are set 156using ajax validation, even if the field is empty. By default, 157validation warnings and errors are I<not> shown for empty fields, as 158yelling to users about mandatory fields they've not gotten to yet is 159poor form. You can use this method to force ajax errors to show even 160on empty fields. 161 162=cut 163 164sub ajax_force_validate { 165 my $self = shift; 166 my $field = shift; 167 $self->{ajax_force_validate}{ $field } = shift if @_; 168 return $self->{ajax_force_validate}{$field}; 169} 170 171=head2 field_canonicalization_note FIELD [NOTE] 172 173Gets or sets a canonicalization note for a specific field on the action. 174 175=cut 176 177sub field_canonicalization_note { 178 my $self = shift; 179 my $field = shift; 180 181 $self->{field_canonicalization_notes}{ $field } = shift if @_; 182 return $self->{field_canonicalization_notes}{ $field }; 183} 184 185=head2 field_canonicalization_notes 186 187Returns a hash which maps L<argument|Jifty::Manual::Glossary/argument> 188name to canonicalization notes. 189 190=cut 191 192sub field_canonicalization_notes { 193 my $self = shift; 194 return %{$self->{field_canonicalization_notes} || {}}; 195} 196 197=head2 content [KEY [, VALUE]] 198 199Gets or sets the content C<KEY>. This is used when actions need to 200return values. If not C<KEY> is passed, it returns an anonymous hash 201of all of the C<KEY> and C<VALUE> pairs. 202 203=cut 204 205sub content { 206 my $self = shift; 207 208 return $self->_content unless @_; 209 210 my $key = shift; 211 $self->_content->{$key} = shift if @_; 212 return $self->_content->{$key}; 213} 214 215=head2 as_hash 216 217This returns the results as a hash to be given directly to the end user 218(usually via REST or webservices). The difference between 219C<< $result->as_hash >> and C<%$result> is that the latter will expand 220everything as deeply as possible. The former won't inflate C<refers_to> 221columns, among other things. 222 223=cut 224 225sub as_hash { 226 my $self = shift; 227 228 my $out = { 229 success => $self->success, 230 failure => $self->failure, 231 action_class => $self->action_class, 232 message => $self->message, 233 error => $self->error, 234 field_errors => { $self->field_errors }, 235 field_warnings => { $self->field_warnings }, 236 content => $self->_recurse_object_to_data($self->content), 237 }; 238 239 for (keys %{$out->{field_errors}}) { 240 delete $out->{field_errors}->{$_} unless $out->{field_errors}->{$_}; 241 } 242 for (keys %{$out->{field_warnings}}) { 243 delete $out->{field_warnings}->{$_} unless $out->{field_warnings}->{$_}; 244 } 245 246 return $out; 247} 248 249sub _recurse_object_to_data { 250 my $self = shift; 251 my $o = shift; 252 253 return $o if !ref($o); 254 255 if (ref($o) eq 'ARRAY') { 256 return [ map { $self->_recurse_object_to_data($_) } @$o ]; 257 } 258 elsif (ref($o) eq 'HASH') { 259 my %h; 260 $h{$_} = $self->_recurse_object_to_data($o->{$_}) for keys %$o; 261 return \%h; 262 } 263 264 return $self->_object_to_data($o); 265} 266 267sub _object_to_data { 268 my $self = shift; 269 my $o = shift; 270 271 if ($o->can('jifty_serialize_format')) { 272 return $o->jifty_serialize_format($self); 273 } 274 275 # As the last resort, return the object itself and expect the 276 # $accept-specific renderer to format the object as e.g. YAML or JSON data. 277 return $o; 278} 279 2801; 281