| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package RapidApp::Util; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # ABSTRACT: Misc util and sugar functions for RapidApp | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 6 |  |  | 6 |  | 137000 | use strict; | 
|  | 6 |  |  |  |  | 30 |  | 
|  | 6 |  |  |  |  | 172 |  | 
| 6 | 6 |  |  | 6 |  | 32 | use warnings; | 
|  | 6 |  |  |  |  | 12 |  | 
|  | 6 |  |  |  |  | 170 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 6 |  |  | 6 |  | 33 | use Scalar::Util qw(blessed weaken reftype); | 
|  | 6 |  |  |  |  | 10 |  | 
|  | 6 |  |  |  |  | 363 |  | 
| 9 | 6 |  |  | 6 |  | 2544 | use Clone qw(clone); | 
|  | 6 |  |  |  |  | 15183 |  | 
|  | 6 |  |  |  |  | 626 |  | 
| 10 | 6 |  |  | 6 |  | 43 | use Carp qw(carp croak confess cluck longmess shortmess); | 
|  | 6 |  |  |  |  | 12 |  | 
|  | 6 |  |  |  |  | 389 |  | 
| 11 | 6 |  |  | 6 |  | 1414 | use Try::Tiny; | 
|  | 6 |  |  |  |  | 5073 |  | 
|  | 6 |  |  |  |  | 326 |  | 
| 12 | 6 |  |  | 6 |  | 2081 | use Time::HiRes qw(gettimeofday tv_interval); | 
|  | 6 |  |  |  |  | 4977 |  | 
|  | 6 |  |  |  |  | 34 |  | 
| 13 | 6 |  |  | 6 |  | 3743 | use Data::Dumper::Concise qw(Dumper); | 
|  | 6 |  |  |  |  | 29906 |  | 
|  | 6 |  |  |  |  | 421 |  | 
| 14 | 6 |  |  | 6 |  | 3622 | use Term::ANSIColor qw(:constants); | 
|  | 6 |  |  |  |  | 50462 |  | 
|  | 6 |  |  |  |  | 7518 |  | 
| 15 | 6 |  |  |  |  | 569 | use RapidApp::JSON::MixedEncoder qw( | 
| 16 |  |  |  |  |  |  | encode_json decode_json encode_json_utf8 decode_json_utf8 encode_json_ascii decode_json_ascii | 
| 17 | 6 |  |  | 6 |  | 3195 | ); | 
|  | 6 |  |  |  |  | 14 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 6 |  |  | 6 |  | 3248 | use RapidApp::Util::Hash::Merge qw( merge ); | 
|  | 6 |  |  |  |  | 16 |  | 
|  | 6 |  |  |  |  | 412 |  | 
| 20 |  |  |  |  |  |  | RapidApp::Util::Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' ); | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 6 |  |  | 6 |  | 4635 | use Data::Printer; | 
|  | 6 |  |  |  |  | 141348 |  | 
|  | 6 |  |  |  |  | 97 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | our $DEBUG_AROUND_COUNT = 0; | 
| 25 |  |  |  |  |  |  | our $DEBUG_AROUND_CALL_NO = 0; | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | BEGIN { | 
| 28 | 6 |  |  | 6 |  | 982 | use Exporter; | 
|  | 6 |  |  |  |  | 13 |  | 
|  | 6 |  |  |  |  | 252 |  | 
| 29 | 6 |  |  | 6 |  | 495 | use parent 'Exporter'; | 
|  | 6 |  |  |  |  | 296 |  | 
|  | 6 |  |  |  |  | 49 |  | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 6 |  |  | 6 |  | 353 | use vars qw (@EXPORT_OK %EXPORT_TAGS); | 
|  | 6 |  |  |  |  | 13 |  | 
|  | 6 |  |  |  |  | 611 |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # These are *extra* exports which came to us via other packages. Note that | 
| 34 |  |  |  |  |  |  | # all functions defined directly in the class will also be added to the | 
| 35 |  |  |  |  |  |  | # @EXPORT_OK and setup with the :all tag (see the end of the file) | 
| 36 | 6 |  |  | 6 |  | 37 | @EXPORT_OK = qw( | 
| 37 |  |  |  |  |  |  | blessed weaken reftype | 
| 38 |  |  |  |  |  |  | clone | 
| 39 |  |  |  |  |  |  | carp croak confess cluck longmess shortmess | 
| 40 |  |  |  |  |  |  | try catch finally | 
| 41 |  |  |  |  |  |  | gettimeofday tv_interval | 
| 42 |  |  |  |  |  |  | Dumper | 
| 43 |  |  |  |  |  |  | encode_json decode_json encode_json_utf8 decode_json_utf8 encode_json_ascii decode_json_ascii | 
| 44 |  |  |  |  |  |  | merge | 
| 45 |  |  |  |  |  |  | ); | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 6 |  |  |  |  | 13 | push @EXPORT_OK, @{$Term::ANSIColor::EXPORT_TAGS{constants}}; | 
|  | 6 |  |  |  |  | 96 |  | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 6 |  |  |  |  | 155 | %EXPORT_TAGS = ( | 
| 50 |  |  |  |  |  |  | all => \@EXPORT_OK | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 | 6 |  |  | 6 |  | 2882 | use RapidApp::Responder::UserError; | 
|  | 6 |  |  |  |  | 24 |  | 
|  | 6 |  |  |  |  | 284 |  | 
| 55 | 6 |  |  | 6 |  | 3652 | use RapidApp::Responder::CustomPrompt; | 
|  | 6 |  |  |  |  | 25 |  | 
|  | 6 |  |  |  |  | 282 |  | 
| 56 | 6 |  |  | 6 |  | 3601 | use RapidApp::Responder::InfoStatus; | 
|  | 6 |  |  |  |  | 26 |  | 
|  | 6 |  |  |  |  | 238 |  | 
| 57 | 6 |  |  | 6 |  | 3066 | use RapidApp::JSONFunc; | 
|  | 6 |  |  |  |  | 27 |  | 
|  | 6 |  |  |  |  | 253 |  | 
| 58 | 6 |  |  | 6 |  | 53 | use RapidApp::JSON::MixedEncoder; | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 412 |  | 
| 59 | 6 |  |  | 6 |  | 3476 | use RapidApp::JSON::RawJavascript; | 
|  | 6 |  |  |  |  | 24 |  | 
|  | 6 |  |  |  |  | 238 |  | 
| 60 | 6 |  |  | 6 |  | 3269 | use RapidApp::JSON::ScriptWithData; | 
|  | 6 |  |  |  |  | 16 |  | 
|  | 6 |  |  |  |  | 197 |  | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 6 |  |  | 6 |  | 2454 | use RapidApp::HTML::RawHtml; | 
|  | 6 |  |  |  |  | 20 |  | 
|  | 6 |  |  |  |  | 171 |  | 
| 63 | 6 |  |  | 6 |  | 2372 | use RapidApp::Handler; | 
|  | 6 |  |  |  |  | 20 |  | 
|  | 6 |  |  |  |  | 244 |  | 
| 64 | 6 |  |  | 6 |  | 56 | use HTML::Entities; | 
|  | 6 |  |  |  |  | 13 |  | 
|  | 6 |  |  |  |  | 409 |  | 
| 65 | 6 |  |  | 6 |  | 3325 | use RapidApp::RootModule; | 
|  | 6 |  |  |  |  | 27 |  | 
|  | 6 |  |  |  |  | 648 |  | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | ######################################################################## | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | sub scream { | 
| 71 | 0 |  |  | 0 | 0 | 0 | local $_ = caller_data(3); | 
| 72 | 0 |  |  |  |  | 0 | scream_color(YELLOW . BOLD,@_); | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub scream_color { | 
| 76 | 0 |  |  | 0 | 0 | 0 | my $color = shift; | 
| 77 | 6 |  |  | 6 |  | 59 | no warnings 'uninitialized'; | 
|  | 6 |  |  |  |  | 14 |  | 
|  | 6 |  |  |  |  | 20930 |  | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 0 |  | 0 |  |  | 0 | my $maxdepth = $Data::Dumper::Maxdepth || 4; | 
| 80 | 0 |  |  |  |  | 0 | local $Data::Dumper::Maxdepth = $maxdepth; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | local $_ = caller_data(3) unless ( | 
| 83 |  |  |  |  |  |  | $_ eq 'no_caller_data' or ( | 
| 84 |  |  |  |  |  |  | ref($_) eq 'ARRAY' and | 
| 85 |  |  |  |  |  |  | scalar(@$_) == 3 and | 
| 86 |  |  |  |  |  |  | ref($_->[0]) eq 'HASH' and | 
| 87 |  |  |  |  |  |  | defined $_->[0]->{package} | 
| 88 |  |  |  |  |  |  | ) | 
| 89 | 0 | 0 | 0 |  |  | 0 | ); | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 0 |  |  |  |  | 0 | my $data = $_[0]; | 
| 92 | 0 | 0 |  |  |  | 0 | $data = \@_ if (scalar(@_) > 1); | 
| 93 | 0 | 0 |  |  |  | 0 | $data = Dumper($data) if (ref $data); | 
| 94 | 0 | 0 |  |  |  | 0 | $data = '  ' . UNDERLINE . 'undef' unless (defined $data); | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 0 |  |  |  |  | 0 | my $pre = ''; | 
| 97 |  |  |  |  |  |  | $pre = BOLD . ($_->[2]->{subroutine} ? $_->[2]->{subroutine} . '  ' : '') . | 
| 98 | 0 | 0 |  |  |  | 0 | '[line ' . $_->[1]->{line} . ']: ' . CLEAR . "\n" unless ($_ eq 'no_caller_data'); | 
|  |  | 0 |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 |  |  |  |  | 0 | print STDERR $pre . $color . $data . CLEAR . "\n"; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 0 |  |  |  |  | 0 | return @_; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | # Takes a list and returns a HashRef. List can be a mixed Hash/List: | 
| 107 |  |  |  |  |  |  | #( | 
| 108 |  |  |  |  |  |  | #  item1 => { opt1 => 'foo' }, | 
| 109 |  |  |  |  |  |  | #  item2 => { key => 'data', foo => 'blah' }, | 
| 110 |  |  |  |  |  |  | #  'item3', | 
| 111 |  |  |  |  |  |  | #  'item4', | 
| 112 |  |  |  |  |  |  | #  item1 => { opt2 => 'foobar', opt3 => 'zippy do da' } | 
| 113 |  |  |  |  |  |  | #) | 
| 114 |  |  |  |  |  |  | # Bare items like item3 and item4 become {} in the returned hashref. | 
| 115 |  |  |  |  |  |  | # Repeated items like item1 and merged | 
| 116 |  |  |  |  |  |  | # also handles the first arg as a hashref or arrayref | 
| 117 |  |  |  |  |  |  | sub get_mixed_hash_args { | 
| 118 | 0 |  |  | 0 | 0 | 0 | my @args = @_; | 
| 119 | 0 | 0 |  |  |  | 0 | return $args[0] if (ref($args[0]) eq 'HASH'); | 
| 120 | 0 | 0 |  |  |  | 0 | @args = @{ $args[0] } if (ref($args[0]) eq 'ARRAY'); | 
|  | 0 |  |  |  |  | 0 |  | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 0 |  |  |  |  | 0 | my $hashref = {}; | 
| 123 | 0 |  |  |  |  | 0 | my $last; | 
| 124 | 0 |  |  |  |  | 0 | foreach my $item (@args) { | 
| 125 | 0 | 0 |  |  |  | 0 | if (ref($item)) { | 
| 126 | 0 | 0 | 0 |  |  | 0 | die "Error in arguments" unless (ref($item) eq 'HASH' and defined $last and not ref($last)); | 
|  |  |  | 0 |  |  |  |  | 
| 127 | 0 |  |  |  |  | 0 | $hashref->{$last} = { %{$hashref->{$last}}, %$item }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 128 | 0 |  |  |  |  | 0 | next; | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 0 |  |  |  |  | 0 | $last = $item; | 
| 131 | 0 | 0 |  |  |  | 0 | $hashref->{$item} = {} unless (defined $hashref->{$item}); | 
| 132 |  |  |  |  |  |  | } | 
| 133 | 0 |  |  |  |  | 0 | return $hashref; | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | # Takes a list and returns a Hash. Like get_mixed_hash_args, but | 
| 138 |  |  |  |  |  |  | # list order is preserved | 
| 139 |  |  |  |  |  |  | sub get_mixed_hash_args_ordered { | 
| 140 | 0 |  |  | 0 | 0 | 0 | my @args = @_; | 
| 141 | 0 | 0 |  |  |  | 0 | return $args[0] if (ref($args[0]) eq 'HASH'); | 
| 142 | 0 | 0 |  |  |  | 0 | @args = @{ $args[0] } if (ref($args[0]) eq 'ARRAY'); | 
|  | 0 |  |  |  |  | 0 |  | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 0 |  |  |  |  | 0 | my $hashref = {}; | 
| 145 | 0 |  |  |  |  | 0 | my @list = (); | 
| 146 | 0 |  |  |  |  | 0 | my $last; | 
| 147 | 0 |  |  |  |  | 0 | foreach my $item (@args) { | 
| 148 | 0 | 0 |  |  |  | 0 | if (ref($item)) { | 
| 149 | 0 | 0 | 0 |  |  | 0 | die "Error in arguments" unless (ref($item) eq 'HASH' and defined $last and not ref($last)); | 
|  |  |  | 0 |  |  |  |  | 
| 150 | 0 |  |  |  |  | 0 | $hashref->{$last} = { %{$hashref->{$last}}, %$item }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 151 | 0 |  |  |  |  | 0 | push @list, $last, $hashref->{$last}; | 
| 152 | 0 |  |  |  |  | 0 | next; | 
| 153 |  |  |  |  |  |  | } | 
| 154 | 0 | 0 |  |  |  | 0 | $hashref->{$item} = {} unless (defined $hashref->{$item}); | 
| 155 | 0 | 0 |  |  |  | 0 | push @list,$item,$hashref->{$item} unless (ref $last); | 
| 156 | 0 |  |  |  |  | 0 | $last = $item; | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 0 |  |  |  |  | 0 | return @list; # <-- preserve order | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | # returns \0 and \1 as 0 and 1, and returns 0 and 1 as 0 and 1 | 
| 163 |  |  |  |  |  |  | sub jstrue { | 
| 164 | 2970 |  |  | 2970 | 0 | 5847 | my $v = shift; | 
| 165 | 2970 | 100 | 66 |  |  | 19025 | ref($v) && ref($v) eq 'SCALAR' ? $$v : $v; | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | # The coderefs supplied here get called immediately after the | 
| 170 |  |  |  |  |  |  | # _load_root_module method in RapidApp/RapidApp.pm | 
| 171 |  |  |  |  |  |  | sub rapidapp_add_global_init_coderef { | 
| 172 | 0 |  |  | 0 | 0 | 0 | foreach my $ref (@_) { | 
| 173 | 0 | 0 |  |  |  | 0 | ref($ref) eq 'CODE' or die "rapidapp_add_global_init_coderef: argument is not a CodeRef: " . Dumper($ref); | 
| 174 | 0 |  |  |  |  | 0 | push @RapidApp::RootModule::GLOBAL_INIT_CODEREFS, $ref; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # Returns an arrayref of hashes containing standard 'caller' function data | 
| 179 |  |  |  |  |  |  | # with named properties: | 
| 180 |  |  |  |  |  |  | sub caller_data { | 
| 181 | 0 |  | 0 | 0 | 0 | 0 | my $depth = shift || 1; | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 0 |  |  |  |  | 0 | my @list = (); | 
| 184 | 0 |  |  |  |  | 0 | for(my $i = 0; $i < $depth; $i++) { | 
| 185 | 0 |  |  |  |  | 0 | my $h = {}; | 
| 186 |  |  |  |  |  |  | ($h->{package}, $h->{filename}, $h->{line}, $h->{subroutine}, $h->{hasargs}, | 
| 187 | 0 |  |  |  |  | 0 | $h->{wantarray}, $h->{evaltext}, $h->{is_require}, $h->{hints}, $h->{bitmask}) = caller($i); | 
| 188 | 0 | 0 |  |  |  | 0 | push @list,$h if($h->{package}); | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 0 |  |  |  |  | 0 | return \@list; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | sub caller_data_brief { | 
| 195 | 0 |  | 0 | 0 | 0 | 0 | my $depth = shift || 1; | 
| 196 | 0 |  |  |  |  | 0 | my $list = caller_data($depth + 1); | 
| 197 | 0 |  |  |  |  | 0 | my $regex = shift; | 
| 198 |  |  |  |  |  |  |  | 
| 199 | 0 |  |  |  |  | 0 | shift @$list; | 
| 200 | 0 |  |  |  |  | 0 | shift @$list; | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 0 |  |  |  |  | 0 | my @inc_parms = qw(subroutine line filename); | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 0 |  |  |  |  | 0 | my %inc = map { $_ => 1 } @inc_parms; | 
|  | 0 |  |  |  |  | 0 |  | 
| 205 |  |  |  |  |  |  |  | 
| 206 | 0 |  |  |  |  | 0 | my @new = (); | 
| 207 | 0 |  |  |  |  | 0 | my $seq = 0; | 
| 208 | 0 |  |  |  |  | 0 | foreach my $item (@$list) { | 
| 209 | 0 | 0 | 0 |  |  | 0 | if($regex and ! eval('$item->{subroutine} =~ /' . $regex . '/')) { | 
| 210 | 0 |  |  |  |  | 0 | $seq++; | 
| 211 | 0 |  |  |  |  | 0 | next; | 
| 212 |  |  |  |  |  |  | } | 
| 213 | 0 | 0 |  |  |  | 0 | push @new, ' . ' x $seq if ($seq); | 
| 214 | 0 |  |  |  |  | 0 | $seq = 0; | 
| 215 | 0 |  |  |  |  | 0 | push @new, { map { $_ => $item->{$_} } grep { $inc{$_} } keys %$item }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 0 |  |  |  |  | 0 | return \@new; | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | # TODO: replace this with uniq from List::Utils | 
| 223 |  |  |  |  |  |  | # Returns a list with duplicates removed. If passed a single arrayref, duplicates are | 
| 224 |  |  |  |  |  |  | # removed from the arrayref in place, and the new list (contents) are returned. | 
| 225 |  |  |  |  |  |  | sub uniq { | 
| 226 | 10027 |  |  | 10027 | 0 | 18288 | my %seen = (); | 
| 227 | 10027 | 50 | 100 |  |  | 37359 | return grep { !$seen{ defined $_ ? $_ : '___!undef!___'}++ } @_ unless (@_ == 1 and ref($_[0]) eq 'ARRAY'); | 
|  | 22305 | 100 |  |  |  | 140927 |  | 
| 228 | 184 | 50 |  |  |  | 376 | return () unless (@{$_[0]} > 0); | 
|  | 184 |  |  |  |  | 589 |  | 
| 229 |  |  |  |  |  |  | # we add the first element to the end of the arg list to prevetn deep recursion in the | 
| 230 |  |  |  |  |  |  | # case of nested single element arrayrefs | 
| 231 | 184 |  |  |  |  | 376 | @{$_[0]} = uniq(@{$_[0]},$_[0]->[0]); | 
|  | 184 |  |  |  |  | 645 |  | 
|  | 184 |  |  |  |  | 773 |  | 
| 232 | 184 |  |  |  |  | 373 | return @{$_[0]}; | 
|  | 184 |  |  |  |  | 462 |  | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | sub deref { | 
| 236 | 0 |  |  | 0 | 0 | 0 | my $ref = shift; | 
| 237 | 0 |  | 0 |  |  | 0 | my $type = ref $ref || return $ref,@_; | 
| 238 | 0 | 0 |  |  |  | 0 | die 'deref(): more than 1 argument not supported' if (@_ > 0); | 
| 239 | 0 | 0 |  |  |  | 0 | return $$ref if ($type eq 'SCALAR'); | 
| 240 | 0 | 0 |  |  |  | 0 | return @$ref if ($type eq 'ARRAY'); | 
| 241 | 0 | 0 |  |  |  | 0 | return %$ref if ($type eq 'HASH'); | 
| 242 | 0 |  |  |  |  | 0 | die "deref(): invalid ref type '$type' - supported types: SCALAR, ARRAY and HASH"; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | # Generic function returns a short display string of a supplied value/values | 
| 246 |  |  |  |  |  |  | # This is like a lite version of Dumper meant more for single values | 
| 247 |  |  |  |  |  |  | # Accepts optional CodeRef as first argument for custom handling, for example, | 
| 248 |  |  |  |  |  |  | # this would allow you to use Dumper instead for all ref values: | 
| 249 |  |  |  |  |  |  | # print disp(sub{ ref $_ ? Dumper($_) : undef },$_) for (@vals); | 
| 250 |  |  |  |  |  |  | sub disp { | 
| 251 | 0 | 0 |  | 0 | 0 | 0 | my $recurse = (caller(1))[3] eq __PACKAGE__ . '::disp' ? 1 : 0; #<-- true if called by ourself | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 0 | 0 |  |  |  | 0 | local $_{code} = $recurse ? $_{code} : undef; | 
| 254 | 0 | 0 | 0 |  |  | 0 | $_{code} = shift if(ref($_[0]) eq 'CODE' && @_>1 && $recurse == 0); | 
|  |  |  | 0 |  |  |  |  | 
| 255 | 0 | 0 |  |  |  | 0 | if($_{code}) { | 
| 256 | 0 |  |  |  |  | 0 | local $_ = $_[0]; | 
| 257 | 0 |  |  |  |  | 0 | my $cust = $_{code}->(@_); | 
| 258 | 0 | 0 |  |  |  | 0 | return $cust if (defined $cust); | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 0 | 0 |  |  |  | 0 | return join(',',map {disp($_)} @_) if(@_>1); | 
|  | 0 |  |  |  |  | 0 |  | 
| 262 | 0 |  |  |  |  | 0 | my $val = shift; | 
| 263 | 0 | 0 |  |  |  | 0 | return 'undef' unless (defined $val); | 
| 264 | 0 | 0 |  |  |  | 0 | if(ref $val) { | 
| 265 | 0 | 0 |  |  |  | 0 | return '[' . disp(@$val) . ']' if (ref($val) eq 'ARRAY'); | 
| 266 | 0 | 0 |  |  |  | 0 | return '\\' . disp($$val) if (ref($val) eq 'SCALAR'); | 
| 267 | 0 | 0 |  |  |  | 0 | return '{ ' . join(',',map { $_ . ' => ' . disp($val->{$_}) } keys %$val) . ' }' if (ref($val) eq 'HASH'); | 
|  | 0 |  |  |  |  | 0 |  | 
| 268 | 0 |  |  |  |  | 0 | return "$val" #<-- generic fall-back for other references | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 0 |  |  |  |  | 0 | return "'" . $val . "'"; | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | sub print_trunc($$) { | 
| 275 | 6 |  |  | 6 | 0 | 14 | my $max_length = shift; | 
| 276 | 6 |  |  |  |  | 11 | my $str = shift; | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 6 | 50 | 33 |  |  | 55 | die "Invalid max length '$max_length'" unless ( | 
|  |  |  | 33 |  |  |  |  | 
| 279 |  |  |  |  |  |  | defined $max_length && | 
| 280 |  |  |  |  |  |  | $max_length =~ /^\d+$/ && | 
| 281 |  |  |  |  |  |  | $max_length > 0 | 
| 282 |  |  |  |  |  |  | ); | 
| 283 |  |  |  |  |  |  |  | 
| 284 | 6 | 100 |  |  |  | 26 | return 'undef' unless (defined $str); | 
| 285 | 3 | 50 |  |  |  | 14 | if (ref $str) { | 
| 286 | 0 |  |  |  |  | 0 | $str = disp($str); | 
| 287 | 0 |  |  |  |  | 0 | $str =~ s/^\'//; | 
| 288 | 0 |  |  |  |  | 0 | $str =~ s/\'$//; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | # escape single quotes: | 
| 292 | 3 |  |  |  |  | 10 | $str =~ s/'/\\'/g; | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | # convert tabs: | 
| 295 | 3 |  |  |  |  | 10 | $str =~ s/\t/   /g; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 3 |  |  |  |  | 8 | my $length = length $str; | 
| 298 | 3 | 50 |  |  |  | 28 | return "'" . $str . "'" if ($length <= $max_length); | 
| 299 | 0 |  |  |  |  | 0 | return "'" . substr($str,0,$max_length) . "'...<$length" . " bytes> "; | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | our $debug_arounds_set = {}; | 
| 303 |  |  |  |  |  |  | our $debug_around_nest_level = 0; | 
| 304 |  |  |  |  |  |  | our $debug_around_last_nest_level = 0; | 
| 305 |  |  |  |  |  |  | our $debug_around_stats = {}; | 
| 306 |  |  |  |  |  |  | our $debug_around_nest_elapse = 0; | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub debug_around($@) { | 
| 309 | 0 |  |  | 0 | 0 | 0 | my ($pkg,$filename,$line) = caller; | 
| 310 | 0 |  |  |  |  | 0 | my $method = shift; | 
| 311 | 0 |  |  |  |  | 0 | my @methods = ( $method ); | 
| 312 | 0 | 0 |  |  |  | 0 | @methods = @$method if (ref($method) eq 'ARRAY'); | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 0 | 0 |  |  |  | 0 | my %opt = (ref($_[0]) eq 'HASH') ? %{ $_[0] } : @_; # <-- arg as hash or hashref | 
|  | 0 |  |  |  |  | 0 |  | 
| 315 |  |  |  |  |  |  |  | 
| 316 | 0 |  |  |  |  | 0 | %opt = ( | 
| 317 |  |  |  |  |  |  | pkg      => $pkg, | 
| 318 |  |  |  |  |  |  | filename    => $filename, | 
| 319 |  |  |  |  |  |  | line      => $line, | 
| 320 |  |  |  |  |  |  | %opt | 
| 321 |  |  |  |  |  |  | ); | 
| 322 |  |  |  |  |  |  |  | 
| 323 | 0 |  |  |  |  | 0 | $pkg = $opt{pkg}; | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 0 |  |  |  |  | 0 | foreach my $method (@methods) { | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 0 |  |  |  |  | 0 | my $package = $pkg; | 
| 328 | 0 |  |  |  |  | 0 | my @namespace = split(/::/,$method); | 
| 329 | 0 | 0 |  |  |  | 0 | if(scalar @namespace > 1) { | 
| 330 | 0 |  |  |  |  | 0 | $method = pop @namespace; | 
| 331 | 0 |  |  |  |  | 0 | $package = join('::',@namespace); | 
| 332 |  |  |  |  |  |  | } | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 0 | 0 |  |  |  | 0 | next if ($debug_arounds_set->{$package . '->' . $method}++); #<-- if its already set | 
| 335 |  |  |  |  |  |  |  | 
| 336 | 0 |  |  |  |  | 0 | eval "require $package;"; | 
| 337 | 0 |  |  |  |  | 0 | my $around = func_debug_around($method, %opt, pkg => $package); | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # It's a Moose class or otherwise already has an 'around' class method: | 
| 340 | 0 | 0 |  |  |  | 0 | if($package->can('around')) { | 
| 341 | 0 |  |  |  |  | 0 | $package->can('around')->($method => $around); | 
| 342 | 0 |  |  |  |  | 0 | next; | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | # The class doesn't have an around method, so we'll setup manually with Class::MOP: | 
| 346 | 0 |  |  |  |  | 0 | my $meta = Class::MOP::Class->initialize($package); | 
| 347 | 0 |  |  |  |  | 0 | $meta->add_around_method_modifier($method => $around) | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | # Returns a coderef - designed to be a Moose around modifier - that will | 
| 352 |  |  |  |  |  |  | # print useful debug info about the given function to which it is attached | 
| 353 |  |  |  |  |  |  | sub func_debug_around { | 
| 354 | 0 |  |  | 0 | 0 | 0 | my $name = shift; | 
| 355 | 0 | 0 |  |  |  | 0 | my %opt = (ref($_[0]) eq 'HASH') ? %{ $_[0] } : @_; # <-- arg as hash or hashref | 
|  | 0 |  |  |  |  | 0 |  | 
| 356 |  |  |  |  |  |  |  | 
| 357 | 0 |  |  |  |  | 0 | my $Id = $DEBUG_AROUND_COUNT++; | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | %opt = ( | 
| 361 |  |  |  |  |  |  | track_stats    => 1, | 
| 362 |  |  |  |  |  |  | time      => 1, | 
| 363 |  |  |  |  |  |  | verbose      => 0, | 
| 364 |  |  |  |  |  |  | verbose_in    => undef, | 
| 365 |  |  |  |  |  |  | verbose_out    => undef, | 
| 366 |  |  |  |  |  |  | newline      => 0, | 
| 367 |  |  |  |  |  |  | list_args    => 0, | 
| 368 |  |  |  |  |  |  | list_out    => 0, | 
| 369 |  |  |  |  |  |  | dump_maxdepth  => 3, | 
| 370 |  |  |  |  |  |  | use_json    => 0, | 
| 371 |  |  |  |  |  |  | stack      => 0, | 
| 372 |  |  |  |  |  |  | instance    => 0, | 
| 373 |  |  |  |  |  |  | color      => GREEN, | 
| 374 |  |  |  |  |  |  | ret_color    => RED.BOLD, | 
| 375 | 0 |  |  | 0 |  | 0 | arg_ignore    => sub { 0 }, # <-- no debug output prited when this returns true | 
| 376 | 0 |  |  | 0 |  | 0 | return_ignore  => sub { 0 },# <-- no debug output prited when this returns true | 
| 377 | 0 |  |  |  |  | 0 | %opt | 
| 378 |  |  |  |  |  |  | ); | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | # around wrapper in %opt to allow the user to pass a different one to use: | 
| 381 |  |  |  |  |  |  | $opt{around} ||= sub { | 
| 382 | 0 |  |  | 0 |  | 0 | my $orig = shift; | 
| 383 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 384 | 0 | 0 |  |  |  | 0 | print STDERR "\n" if ($opt{newline}); | 
| 385 | 0 |  |  |  |  | 0 | return $self->$orig(@_); | 
| 386 | 0 |  | 0 |  |  | 0 | }; | 
| 387 |  |  |  |  |  |  |  | 
| 388 | 0 | 0 | 0 |  |  | 0 | $opt{verbose_in} = 1 if ($opt{verbose} and not defined $opt{verbose_in}); | 
| 389 | 0 | 0 | 0 |  |  | 0 | $opt{verbose_out} = 1 if ($opt{verbose} and not defined $opt{verbose_out}); | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | $opt{dump_func} = sub { | 
| 392 | 0 |  |  | 0 |  | 0 | my $verbose = shift; | 
| 393 | 0 | 0 | 0 |  |  | 0 | return UNDERLINE . 'undef' . CLEAR unless (@_ > 0 and defined $_[0]); | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | # if list_out is false, return the number of items in the return, underlined | 
| 396 | 0 | 0 |  |  |  | 0 | return $opt{list_out} ? join(',',map { ref $_ ? "$_" : "'$_'" } @_) : UNDERLINE . @_ . CLEAR | 
|  | 0 | 0 |  |  |  | 0 |  | 
|  |  | 0 |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | unless ($verbose); | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 0 |  |  |  |  | 0 | local $Data::Dumper::Maxdepth = $opt{dump_maxdepth}; | 
| 400 | 0 | 0 |  |  |  | 0 | return Dumper(@_) unless ($opt{use_json}); | 
| 401 |  |  |  |  |  |  | #return RapidApp::JSON::MixedEncoder->new->allow_blessed->convert_blessed->allow_nonref->encode(\@_); | 
| 402 | 0 |  |  |  |  | 0 | return encode_json(\@_); | 
| 403 | 0 | 0 |  |  |  | 0 | } unless ($opt{dump_func}); | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | return sub { | 
| 406 | 0 |  |  | 0 |  | 0 | my $orig = shift; | 
| 407 | 0 |  |  |  |  | 0 | my $self = shift; | 
| 408 | 0 |  |  |  |  | 0 | my @args = @_; | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 0 |  |  |  |  | 0 | my $printed_newlines = 0; | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | my $_PRINTER = sub { | 
| 413 | 0 |  |  |  |  | 0 | for my $text (@_) { | 
| 414 | 0 |  |  |  |  | 0 | my $char = "\n"; | 
| 415 | 0 |  |  |  |  | 0 | my $newlines = () = $text =~ /\Q$char/g; | 
| 416 | 0 |  |  |  |  | 0 | $printed_newlines = $printed_newlines + $newlines; | 
| 417 | 0 |  |  |  |  | 0 | print STDERR $text | 
| 418 |  |  |  |  |  |  | } | 
| 419 | 0 |  |  |  |  | 0 | }; | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 0 |  |  |  |  | 0 | my $Count = $DEBUG_AROUND_CALL_NO++; | 
| 422 | 0 |  |  |  |  | 0 | my $is_odd = $Count % 2 == 1; | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 0 | 0 |  |  |  | 0 | my $label_color = $is_odd ? CLEAR.CYAN.BOLD : CLEAR.MAGENTA.BOLD; | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 0 |  |  |  |  | 0 | my $nest_level = $debug_around_nest_level; | 
| 427 | 0 |  |  |  |  | 0 | local $debug_around_nest_level = $debug_around_nest_level + 1; | 
| 428 |  |  |  |  |  |  |  | 
| 429 | 0 | 0 |  |  |  | 0 | my $new_nest = $debug_around_last_nest_level < $nest_level ? 1 : 0; | 
| 430 | 0 | 0 |  |  |  | 0 | my $leave_nest = $debug_around_last_nest_level > $nest_level ? 1 : 0; | 
| 431 | 0 |  |  |  |  | 0 | $debug_around_last_nest_level = $nest_level; | 
| 432 |  |  |  |  |  |  |  | 
| 433 | 0 | 0 |  |  |  | 0 | $debug_around_nest_elapse = 0 if ($nest_level == 0); | 
| 434 |  |  |  |  |  |  |  | 
| 435 | 0 | 0 |  |  |  | 0 | my $indent = $nest_level > 0 ? ('  ' x $nest_level) : ''; | 
| 436 | 0 |  |  |  |  | 0 | my $newline = "\n$indent"; | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 0 |  |  |  |  | 0 | my $has_refs = 0; | 
| 439 |  |  |  |  |  |  |  | 
| 440 | 0 |  |  |  |  | 0 | my $class = $opt{pkg}; | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 0 |  | 0 |  |  | 0 | my $oneline = ! $leave_nest || ! $nest_level; | 
| 443 |  |  |  |  |  |  |  | 
| 444 | 0 | 0 |  |  |  | 0 | $_PRINTER->($newline) if ($new_nest); | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | $_PRINTER->(join('', | 
| 447 |  |  |  |  |  |  | $label_color,"[$Id/$Count]",'==> ',CLEAR,$opt{color},$class,CLEAR,'->', | 
| 448 | 0 |  |  |  |  | 0 | $opt{color},BOLD,$name,CLEAR, | 
| 449 |  |  |  |  |  |  | '( ' . MAGENTA . 'args in: ' . BOLD . scalar(@args) .  CLEAR . ' ) ' | 
| 450 |  |  |  |  |  |  | )); | 
| 451 |  |  |  |  |  |  |  | 
| 452 | 0 | 0 |  |  |  | 0 | if($opt{list_args}) { | 
| 453 | 0 |  |  |  |  | 0 | $oneline = 0; | 
| 454 | 0 |  |  |  |  | 0 | my @plines = split(/\r?\n/,np(@args, colored => 0)); | 
| 455 | 0 |  |  |  |  | 0 | $plines[0] = "Supplied arguments: $plines[0]"; | 
| 456 | 0 |  |  |  |  | 0 | my $max = 0; | 
| 457 | 0 |  | 0 |  |  | 0 | $max < $_ and $max = $_ for (map { length($_) } @plines); | 
|  | 0 |  |  |  |  | 0 |  | 
| 458 |  |  |  |  |  |  |  | 
| 459 | 0 |  |  |  |  | 0 | for my $line (@plines) { | 
| 460 | 0 |  |  |  |  | 0 | my $pad = $max - length($line); | 
| 461 | 0 |  |  |  |  | 0 | $_PRINTER->(join('',$newline,(' ' x ($nest_level+6)), ON_CYAN,'  ', $line,ON_CYAN, (' ' x ($pad+3)),'  ',CLEAR)); | 
| 462 |  |  |  |  |  |  | } | 
| 463 | 0 |  |  |  |  | 0 | $_PRINTER->($newline); | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | #my $in = '( ' . MAGENTA . 'args in: ' . BOLD . scalar(@args) .  CLEAR . ' ): '; | 
| 473 |  |  |  |  |  |  | #if($opt{list_args}) { | 
| 474 |  |  |  |  |  |  | #  my @print_args = map { (ref($_) and ++$has_refs) ? "$_" : MAGENTA . "'$_'" . CLEAR } @args; | 
| 475 |  |  |  |  |  |  | #  $in = '(' . join(',',@print_args) . '): '; | 
| 476 |  |  |  |  |  |  | #} | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 0 | 0 |  |  |  | 0 | if($opt{stack}) { | 
| 480 | 0 |  |  |  |  | 0 | $oneline = 0; | 
| 481 | 0 |  |  |  |  | 0 | my $stack = caller_data_brief($opt{stack} + 3); | 
| 482 | 0 |  |  |  |  | 0 | shift @$stack; | 
| 483 | 0 |  |  |  |  | 0 | shift @$stack; | 
| 484 | 0 |  |  |  |  | 0 | shift @$stack; | 
| 485 | 0 |  |  |  |  | 0 | @$stack = reverse @$stack; | 
| 486 | 0 |  |  |  |  | 0 | my $i = scalar @$stack; | 
| 487 |  |  |  |  |  |  | #my $i = $opt{stack}; | 
| 488 | 0 |  |  |  |  | 0 | $_PRINTER->($newline); | 
| 489 |  |  |  |  |  |  |  | 
| 490 | 0 |  |  |  |  | 0 | my $max_fn = 0; | 
| 491 | 0 |  |  |  |  | 0 | foreach my $data (@$stack) { | 
| 492 | 0 |  |  |  |  | 0 | my ($fn) = split(/\s+/,(reverse split(/\//,$data->{filename}))[0]); | 
| 493 | 0 | 0 |  |  |  | 0 | $max_fn = length($fn) if (length($fn) > $max_fn); | 
| 494 | 0 |  |  |  |  | 0 | $data->{fn} = $fn; | 
| 495 |  |  |  |  |  |  | } | 
| 496 |  |  |  |  |  |  |  | 
| 497 | 0 |  |  |  |  | 0 | my $pfx = ' '; | 
| 498 | 0 |  |  |  |  | 0 | foreach my $data (@$stack) { | 
| 499 |  |  |  |  |  |  | $_PRINTER->($label_color,'|'.$pfx . CLEAR . sprintf("%3s",$i--) . ' | ' . CYAN . sprintf("%".$max_fn."s",$data->{fn}) . ' ' . | 
| 500 |  |  |  |  |  |  | BOLD . sprintf("%-5s",$data->{line}) . CLEAR . CYAN . '-> ' . CLEAR . | 
| 501 | 0 |  |  |  |  | 0 | GREEN . $data->{subroutine} . CLEAR . $newline); | 
| 502 | 0 |  |  |  |  | 0 | $pfx = '^'; | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | #print STDERR '((stack  0)) ' .  sprintf("%7s",'[' . $opt{line} . ']') . ' ' . | 
| 507 |  |  |  |  |  |  | #  GREEN . $class . '::' . $name . $newline . CLEAR; | 
| 508 |  |  |  |  |  |  | #$class = "$self"; | 
| 509 |  |  |  |  |  |  | } | 
| 510 |  |  |  |  |  |  | #else { | 
| 511 |  |  |  |  |  |  | #  print STDERR $newline and $oneline = 0 if ($new_nest); | 
| 512 |  |  |  |  |  |  | #} | 
| 513 |  |  |  |  |  |  |  | 
| 514 | 0 | 0 |  |  |  | 0 | if($opt{stack}) { | 
| 515 | 0 |  |  |  |  | 0 | $_PRINTER->(CLEAR . $label_color . "|^" .CLEAR . BOLD "  -->" . CLEAR); | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 | 0 | 0 |  |  |  | 0 | unless($oneline) { | 
| 519 | 0 | 0 |  |  |  | 0 | $_PRINTER->($label_color . "[$Id/$Count]",'^^  ' . CLEAR) unless ($opt{stack}); | 
| 520 | 0 |  |  |  |  | 0 | $_PRINTER->(' ',$opt{color}  . $class . CLEAR . '->' . $opt{color} . BOLD . $name . ' ' . CLEAR); | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  |  | 
| 534 | 0 |  |  |  |  | 0 | my $spaces = ' ' x (2 + length($opt{line})); | 
| 535 |  |  |  |  |  |  | my $in_func = sub { | 
| 536 |  |  |  |  |  |  | $_PRINTER->($newline . ON_WHITE.BOLD . BLUE . "$spaces Supplied arguments dump: " . | 
| 537 |  |  |  |  |  |  | $opt{dump_func}->($opt{verbose_in},\@args) . CLEAR . $newline . ": ") | 
| 538 | 0 | 0 | 0 |  |  | 0 | if($has_refs && $opt{verbose_in}); | 
| 539 | 0 |  |  |  |  | 0 | }; | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  |  | 
| 547 | 0 |  |  |  |  | 0 | my $res; | 
| 548 |  |  |  |  |  |  | my @res; | 
| 549 | 0 |  |  |  |  | 0 | my @res_copy = (); | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | # before timestamp: | 
| 552 | 0 |  |  |  |  | 0 | my $t0 = [gettimeofday]; | 
| 553 | 0 |  |  |  |  | 0 | my $current_nest_elapse; | 
| 554 |  |  |  |  |  |  | { | 
| 555 | 0 |  |  |  |  | 0 | local $debug_around_nest_elapse = 0; | 
|  | 0 |  |  |  |  | 0 |  | 
| 556 | 0 | 0 |  |  |  | 0 | if(wantarray) { | 
| 557 |  |  |  |  |  |  | try { | 
| 558 | 0 |  |  |  |  | 0 | @res = $opt{around}->($orig,$self,@args); | 
| 559 | 0 |  |  |  |  | 0 | } catch { $in_func->(); die (shift);}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 560 | 0 |  |  |  |  | 0 | push @res_copy, @res; | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  | else { | 
| 563 |  |  |  |  |  |  | try { | 
| 564 | 0 |  |  |  |  | 0 | $res = $opt{around}->($orig,$self,@args); | 
| 565 | 0 |  |  |  |  | 0 | } catch { $in_func->(); die (shift);}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 566 | 0 |  |  |  |  | 0 | push @res_copy,$res; | 
| 567 |  |  |  |  |  |  | } | 
| 568 |  |  |  |  |  |  | # How much of the elapsed time was in nested funcs below us: | 
| 569 | 0 |  |  |  |  | 0 | $current_nest_elapse = $debug_around_nest_elapse; | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  |  | 
| 574 | 0 | 0 |  |  |  | 0 | if($opt{list_out}) { | 
| 575 | 0 |  |  |  |  | 0 | $oneline = 0; | 
| 576 | 0 |  |  |  |  | 0 | my @plines = split(/\r?\n/,np(@res_copy, colored => 0)); | 
| 577 | 0 |  |  |  |  | 0 | $plines[0] = "Returned values: $plines[0]"; | 
| 578 | 0 |  |  |  |  | 0 | my $max = 0; | 
| 579 | 0 |  | 0 |  |  | 0 | $max < $_ and $max = $_ for (map { length($_) } @plines); | 
|  | 0 |  |  |  |  | 0 |  | 
| 580 |  |  |  |  |  |  |  | 
| 581 | 0 |  |  |  |  | 0 | for my $line (@plines) { | 
| 582 | 0 |  |  |  |  | 0 | my $pad = $max - length($line); | 
| 583 | 0 |  |  |  |  | 0 | $_PRINTER->(join('',$newline,(' ' x ($nest_level+6)), ON_GREEN,'  ', $line,ON_GREEN, (' ' x ($pad+3)),'  ',CLEAR)); | 
| 584 |  |  |  |  |  |  | } | 
| 585 | 0 |  |  |  |  | 0 | $_PRINTER->($newline); | 
| 586 |  |  |  |  |  |  | } | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | # after timestamp, calculate elapsed (to the millisecond): | 
| 593 | 0 |  |  |  |  | 0 | my $elapsed_raw = tv_interval($t0); | 
| 594 | 0 |  |  |  |  | 0 | my $adj_elapsed = $elapsed_raw - $current_nest_elapse; | 
| 595 | 0 |  |  |  |  | 0 | $debug_around_nest_elapse += $elapsed_raw; #<-- send our elapsed time up the chain | 
| 596 |  |  |  |  |  |  |  | 
| 597 | 0 | 0 |  |  |  | 0 | if($opt{list_out}) { | 
| 598 |  |  |  |  |  |  | $_PRINTER->($label_color . $label_color . "[$Id/$Count]", '^^^ ' . CLEAR . $opt{color}  . $class . CLEAR . '->' . | 
| 599 | 0 |  |  |  |  | 0 | $opt{color} . BOLD . $name . ' '); | 
| 600 |  |  |  |  |  |  | } | 
| 601 |  |  |  |  |  |  |  | 
| 602 | 0 |  |  |  |  | 0 | $_PRINTER->($opt{ret_color} . 'Ret itms: ' . scalar(@res_copy) . CLEAR); | 
| 603 | 0 |  |  |  |  | 0 | $_PRINTER->(CLEAR . ' in ' . ON_WHITE.RED . sprintf('%.5fs',$elapsed_raw) . ' (' . sprintf('%.5fs',$adj_elapsed) . ' exclusive)' . CLEAR); | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | # -- Track stats in global %$RapidApp::Util::debug_around_stats: | 
| 607 | 0 | 0 |  |  |  | 0 | if($opt{track_stats}) { | 
| 608 | 6 |  |  | 6 |  | 51 | no warnings 'uninitialized'; | 
|  | 6 |  |  |  |  | 15 |  | 
|  | 6 |  |  |  |  | 11758 |  | 
| 609 | 0 |  |  |  |  | 0 | my $k = $class . '->' . $name; | 
| 610 | 0 |  | 0 |  |  | 0 | $debug_around_stats->{$k} = $debug_around_stats->{$k} || {}; | 
| 611 | 0 |  |  |  |  | 0 | my $stats = $debug_around_stats->{$k}; | 
| 612 |  |  |  |  |  |  | %$stats = ( | 
| 613 |  |  |  |  |  |  | class => $class, | 
| 614 |  |  |  |  |  |  | sub => $name, | 
| 615 |  |  |  |  |  |  | line => $opt{line}, | 
| 616 |  |  |  |  |  |  | calls => $stats->{calls} + 1, | 
| 617 |  |  |  |  |  |  | real_total => $stats->{real_total} + $elapsed_raw, | 
| 618 |  |  |  |  |  |  | total => $stats->{total} + $adj_elapsed, | 
| 619 |  |  |  |  |  |  | min => exists $stats->{min} ? $stats->{min} : $adj_elapsed, | 
| 620 | 0 | 0 |  |  |  | 0 | max => exists $stats->{max} ? $stats->{max} : $adj_elapsed, | 
|  |  | 0 |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | ); | 
| 622 | 0 |  |  |  |  | 0 | $stats->{avg} = $stats->{total}/$stats->{calls}; | 
| 623 | 0 | 0 |  |  |  | 0 | $stats->{min} = $adj_elapsed if ($adj_elapsed < $stats->{min}); | 
| 624 | 0 | 0 |  |  |  | 0 | $stats->{max} = $adj_elapsed if ($adj_elapsed > $stats->{max}); | 
| 625 |  |  |  |  |  |  | } | 
| 626 |  |  |  |  |  |  | # -- | 
| 627 |  |  |  |  |  |  |  | 
| 628 | 0 |  |  |  |  | 0 | local $_ = $self; | 
| 629 | 0 | 0 | 0 |  |  | 0 | if(!$opt{arg_ignore}->(@args) && !$opt{return_ignore}->(@res_copy)) { | 
| 630 |  |  |  |  |  |  |  | 
| 631 | 0 |  |  |  |  | 0 | $in_func->(); | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | #my $elapsed_short = '[' . sprintf("%.3f", $elapsed_raw ) . 's]'; | 
| 634 |  |  |  |  |  |  |  | 
| 635 | 0 |  |  |  |  | 0 | my @a = map { sprintf('%.3f',$_) } ($elapsed_raw,$adj_elapsed); | 
|  | 0 |  |  |  |  | 0 |  | 
| 636 | 0 |  |  |  |  | 0 | my $elapsed_long = '[' . join('|',@a) . ']'; | 
| 637 |  |  |  |  |  |  |  | 
| 638 | 0 |  |  |  |  | 0 | my $result = $opt{ret_color} . $opt{dump_func}->($opt{verbose_out},@res_copy) . CLEAR; | 
| 639 | 0 | 0 |  |  |  | 0 | $result = "\n" . ON_WHITE.BOLD . "$spaces Returned: " . $result . "\n" if ($opt{verbose_out}); | 
| 640 | 0 | 0 |  |  |  | 0 | $result .= ' ' . ON_WHITE.RED . $elapsed_long . CLEAR if ($opt{time}); | 
| 641 |  |  |  |  |  |  |  | 
| 642 | 0 |  |  |  |  | 0 | $result =~ s/\n/${newline}/gm; | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | # Reset cursor position if nesting happened: | 
| 645 | 0 | 0 |  |  |  | 0 | $_PRINTER->("\r$indent") unless ($RapidApp::Util::debug_around_last_nest_level == $nest_level); | 
| 646 |  |  |  |  |  |  |  | 
| 647 |  |  |  |  |  |  | #print STDERR $result . $newline; | 
| 648 | 0 |  |  |  |  | 0 | $_PRINTER->($newline); | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  | else { | 
| 652 |  |  |  |  |  |  | # 'arg_ignore' and/or 'return_ignore' returned true, so we're not | 
| 653 |  |  |  |  |  |  | # supposed to print anything... but since we already have, in case | 
| 654 |  |  |  |  |  |  | # the function would have barfed, we'll print a \r to move the cursor | 
| 655 |  |  |  |  |  |  | # to the begining of the line so it will get overwritten, which is | 
| 656 |  |  |  |  |  |  | # almost as good as if we had not printed anything in the first place... | 
| 657 |  |  |  |  |  |  | # (note if the function printed something too we're screwed) | 
| 658 | 0 |  |  |  |  | 0 | $_PRINTER->("\r"); | 
| 659 |  |  |  |  |  |  | } | 
| 660 |  |  |  |  |  |  |  | 
| 661 | 0 | 0 |  |  |  | 0 | if($printed_newlines > 5) { | 
| 662 | 0 |  |  |  |  | 0 | $_PRINTER->($label_color,"[$Id/$Count]", ('-' x 80), '^^^^', "\n\n",CLEAR); | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  |  | 
| 666 | 0 | 0 |  |  |  | 0 | return wantarray ? @res : $res; | 
| 667 | 0 |  |  |  |  | 0 | }; | 
| 668 |  |  |  |  |  |  | } | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | # Lets you create a sub and set debug_around on it at the same time | 
| 671 |  |  |  |  |  |  | sub debug_sub($&) { | 
| 672 | 0 |  |  | 0 | 0 | 0 | my ($pkg,$filename,$line) = caller; | 
| 673 | 0 |  |  |  |  | 0 | my ($name,$code) = @_; | 
| 674 |  |  |  |  |  |  |  | 
| 675 | 0 |  |  |  |  | 0 | my $meta = Class::MOP::Class->initialize($pkg); | 
| 676 | 0 |  |  |  |  | 0 | $meta->add_method($name,$code); | 
| 677 |  |  |  |  |  |  |  | 
| 678 | 0 |  |  |  |  | 0 | return debug_around $name, pkg => $pkg, filename => $filename, line => $line; | 
| 679 |  |  |  |  |  |  | } | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | sub debug_around_all { | 
| 682 | 0 |  | 0 | 0 | 0 | 0 | my $pkg = shift || caller; | 
| 683 | 0 |  |  |  |  | 0 | my $meta = Class::MOP::Class->initialize($pkg); | 
| 684 | 0 |  |  |  |  | 0 | debug_around($_, pkg => $pkg) for ($meta->get_method_list); | 
| 685 |  |  |  |  |  |  | } | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | # Returns a stat in a hash with named keys | 
| 688 |  |  |  |  |  |  | sub xstat { | 
| 689 | 0 |  |  | 0 | 0 | 0 | my $file = shift; | 
| 690 | 0 | 0 |  |  |  | 0 | return undef unless (-e $file); | 
| 691 | 0 |  |  |  |  | 0 | my $h = {}; | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | ($h->{dev},$h->{ino},$h->{mode},$h->{nlink},$h->{uid},$h->{gid},$h->{rdev}, | 
| 694 |  |  |  |  |  |  | $h->{size},$h->{atime},$h->{mtime},$h->{ctime},$h->{blksize},$h->{blocks}) | 
| 695 | 0 |  |  |  |  | 0 | = stat($file); | 
| 696 |  |  |  |  |  |  |  | 
| 697 | 0 |  |  |  |  | 0 | return $h; | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | ##### From RapidApp::Sugar ##### | 
| 702 |  |  |  |  |  |  |  | 
| 703 |  |  |  |  |  |  | sub asjson { | 
| 704 | 0 | 0 |  | 0 | 0 | 0 | scalar(@_) == 1 or die "Expected single argument"; | 
| 705 | 0 |  |  |  |  | 0 | return RapidApp::JSON::MixedEncoder::encode_json($_[0]); | 
| 706 |  |  |  |  |  |  | } | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | # Bless a string as RawJavascript so that it doesn't get encoded as JSON data during asjson | 
| 709 |  |  |  |  |  |  | sub rawjs { | 
| 710 | 0 | 0 | 0 | 0 | 0 | 0 | scalar(@_) == 1 && ref $_[0] eq '' or die "Expected single string argument"; | 
| 711 | 0 |  |  |  |  | 0 | return RapidApp::JSON::RawJavascript->new(js=>$_[0]); | 
| 712 |  |  |  |  |  |  | } | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | # Works like rawjs but accepts a list of arguments. Each argument should be a function defintion, | 
| 715 |  |  |  |  |  |  | # and will be stacked together, passing each function in the chain through the first argument | 
| 716 |  |  |  |  |  |  | sub jsfunc { | 
| 717 | 2093 | 50 |  | 2093 | 0 | 4871 | my $js = shift or die "jsfunc(): At least one argument is required"; | 
| 718 |  |  |  |  |  |  |  | 
| 719 | 2093 | 100 |  |  |  | 5007 | return jsfunc(@$js) if (ref($js) eq 'ARRAY'); | 
| 720 |  |  |  |  |  |  |  | 
| 721 | 1509 | 50 | 66 |  |  | 4017 | blessed $js and not $js->can('TO_JSON_RAW') and | 
| 722 |  |  |  |  |  |  | die "jsfunc: arguments must be JavaScript function definition strings or objects with TO_JSON_RAW methods"; | 
| 723 |  |  |  |  |  |  |  | 
| 724 | 1509 | 100 |  |  |  | 3287 | $js = $js->TO_JSON_RAW if (blessed $js); | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | # Remove undef arguments: | 
| 727 | 1509 |  |  |  |  | 2483 | @_ = grep { defined $_ } @_; | 
|  | 918 |  |  |  |  | 3053 |  | 
| 728 |  |  |  |  |  |  |  | 
| 729 | 1509 | 100 |  |  |  | 4787 | $js = 'function(){ ' . | 
| 730 |  |  |  |  |  |  | 'var args = arguments; ' . | 
| 731 |  |  |  |  |  |  | 'args[0] = (' . $js . ').apply(this,arguments); ' . | 
| 732 |  |  |  |  |  |  | 'return (' . jsfunc(@_) . ').apply(this,args); ' . | 
| 733 |  |  |  |  |  |  | '}' if (scalar @_ > 0); | 
| 734 |  |  |  |  |  |  |  | 
| 735 | 1509 |  |  |  |  | 46379 | return RapidApp::JSON::RawJavascript->new(js=>$js) | 
| 736 |  |  |  |  |  |  | } | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | # Encode a mix of javascript and data into appropriate objects that will get converted | 
| 739 |  |  |  |  |  |  | #  to JSON properly during "asjson". | 
| 740 |  |  |  |  |  |  | # | 
| 741 |  |  |  |  |  |  | # Example:  mixedjs "function() { var data=", { a => $foo, b => $bar }, "; Ext.msg.alert(data); }"; | 
| 742 |  |  |  |  |  |  | # See ScriptWithData for more details. | 
| 743 |  |  |  |  |  |  | # | 
| 744 |  |  |  |  |  |  | sub mixedjs { | 
| 745 | 0 |  |  | 0 | 0 |  | return RapidApp::JSON::ScriptWithData->new(@_); | 
| 746 |  |  |  |  |  |  | } | 
| 747 |  |  |  |  |  |  |  | 
| 748 |  |  |  |  |  |  | # Take a string of text/plain and convert it to text/html.  This handles "RawHtml" objects. | 
| 749 |  |  |  |  |  |  | sub ashtml { | 
| 750 | 0 |  |  | 0 | 0 |  | my $text= shift; | 
| 751 | 0 | 0 | 0 |  |  |  | return "$text" if ref($text) && ref($text)->isa('RapidApp::HTML::RawHtml'); | 
| 752 | 0 | 0 |  |  |  |  | return undef unless defined $text; | 
| 753 | 0 |  |  |  |  |  | return join('<br />', map { encode_entities($_) } split("\n", "$text")); | 
|  | 0 |  |  |  |  |  |  | 
| 754 |  |  |  |  |  |  | } | 
| 755 |  |  |  |  |  |  |  | 
| 756 |  |  |  |  |  |  | # Bless a scalar to indicate the scalar is already html, and doesn't need converted. | 
| 757 |  |  |  |  |  |  | sub rawhtml { | 
| 758 | 0 |  |  | 0 | 0 |  | my $html= shift; | 
| 759 |  |  |  |  |  |  | # any other arguments we were given, we pass back in hopes that we're part of a function call that needed them. | 
| 760 | 0 |  |  |  |  |  | return RapidApp::HTML::RawHtml->new($html), @_; | 
| 761 |  |  |  |  |  |  | } | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | =head2 usererr $message, key => $value, key => $value | 
| 764 |  |  |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | Shorthand notation to create a UserError, to inform the user they did something wrong. | 
| 766 |  |  |  |  |  |  | First argument is a scalar of text (or a RawHtml scalar of html) | 
| 767 |  |  |  |  |  |  | Second through N arguments are hash keys to apply to the UserError constructor. | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | Examples: | 
| 770 |  |  |  |  |  |  | # To throw a message to the user with no data and no error report: | 
| 771 |  |  |  |  |  |  | die usererr "Hey you moron, don't do that"; | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | # To specify that your message is html already: | 
| 774 |  |  |  |  |  |  | die usererr rawhtml "<h2>Hell Yeah</h2>"; | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | =cut | 
| 777 |  |  |  |  |  |  |  | 
| 778 |  |  |  |  |  |  | my %keyAliases = ( | 
| 779 |  |  |  |  |  |  | msg => 'message', | 
| 780 |  |  |  |  |  |  | umsg => 'userMessage', | 
| 781 |  |  |  |  |  |  | title => 'userMessageTitle', | 
| 782 |  |  |  |  |  |  | ); | 
| 783 |  |  |  |  |  |  | sub usererr { | 
| 784 | 0 |  |  | 0 | 1 |  | my %args= (); | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | # First arg is always the message.  We stringify it, so it doesn't matter if it was an object. | 
| 787 | 0 |  |  |  |  |  | my $msg= shift; | 
| 788 | 0 | 0 |  |  |  |  | defined $msg or die "userexception requires at least a first message argument"; | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | # If the passed arg is already a UserError object, return it as-is: | 
| 791 | 0 | 0 | 0 |  |  |  | return $msg if ref($msg) && ref($msg)->isa('RapidApp::Responder::UserError'); | 
| 792 |  |  |  |  |  |  |  | 
| 793 | 0 | 0 | 0 |  |  |  | $args{userMessage}= ref($msg) && ref($msg)->isa('RapidApp::HTML::RawHtml')? $msg : "$msg"; | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | # pull in any other args | 
| 796 | 0 |  |  |  |  |  | while (scalar(@_) > 1) { | 
| 797 | 0 |  |  |  |  |  | my ($key, $val)= (shift, shift); | 
| 798 | 0 |  | 0 |  |  |  | $key = $keyAliases{$key} || $key; | 
| 799 | 0 | 0 |  |  |  |  | RapidApp::Responder::UserError->can($key) | 
| 800 |  |  |  |  |  |  | or warn "Invalid attribute for UserError: $key"; | 
| 801 | 0 |  |  |  |  |  | $args{$key}= $val; | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | # userexception is allowed to have a payload at the end, but this would be meaningless for usererr, | 
| 805 |  |  |  |  |  |  | #  since usererr is not saved. | 
| 806 | 0 | 0 |  |  |  |  | if (scalar(@_)) { | 
| 807 | 0 |  |  |  |  |  | my ($pkg, $file, $line)= caller; | 
| 808 | 0 |  |  |  |  |  | warn "Odd number of arguments to usererr at $file:$line"; | 
| 809 |  |  |  |  |  |  | } | 
| 810 |  |  |  |  |  |  |  | 
| 811 | 0 |  |  |  |  |  | return RapidApp::Responder::UserError->new(\%args); | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | =head2 userexception $message, key => $value, key => $value, \%data | 
| 815 |  |  |  |  |  |  |  | 
| 816 |  |  |  |  |  |  | Shorthand notation for creating a RapidApp::Error which also informs the user about why the error occured. | 
| 817 |  |  |  |  |  |  | First argument is the message displayed to the user (can be a RawHtml object). | 
| 818 |  |  |  |  |  |  | Last argument is a hash of data that should be saved for the error report. | 
| 819 |  |  |  |  |  |  | ( the last argument is equivalent to a value for an implied hash key of "data" ) | 
| 820 |  |  |  |  |  |  |  | 
| 821 |  |  |  |  |  |  | Examples: | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | # Die with a custom user-facing message (in plain text), and a title made of html. | 
| 824 |  |  |  |  |  |  | die userexception "Description of what shouldn't have happened", title => rawhtml "<h1>ERROR</h1>"; | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | # Capture some data for the error report, as we show this message to the user. | 
| 827 |  |  |  |  |  |  | die userexception "Description of what shouldn't have happened", $some_debug_info; | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | =cut | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | sub userexception { | 
| 832 | 0 |  |  | 0 | 1 |  | my %args= (); | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | # First arg is always the message.  We stringify it, so it doesn't matter if it was an object. | 
| 835 | 0 |  |  |  |  |  | my $msg= shift; | 
| 836 | 0 | 0 |  |  |  |  | defined $msg or die "userexception requires at least a first message argument"; | 
| 837 | 0 | 0 | 0 |  |  |  | $args{userMessage}= ref($msg) && ref($msg)->isa('RapidApp::HTML::RawHtml')? $msg : "$msg"; | 
| 838 | 0 |  |  |  |  |  | $args{message}= $args{userMessage}; | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | # pull in any other args | 
| 841 | 0 |  |  |  |  |  | while (scalar(@_) > 1) { | 
| 842 | 0 |  |  |  |  |  | my ($key, $val)= (shift, shift); | 
| 843 | 0 |  | 0 |  |  |  | $key = $keyAliases{$key} || $key; | 
| 844 | 0 | 0 |  |  |  |  | RapidApp::Error->can($key) | 
| 845 |  |  |  |  |  |  | or warn "Invalid attribute for RapidApp::Error: $key"; | 
| 846 | 0 |  |  |  |  |  | $args{$key}= $val; | 
| 847 |  |  |  |  |  |  | } | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | # userexception is allowed to have a payload as the last argument | 
| 850 | 0 | 0 |  |  |  |  | if (scalar(@_)) { | 
| 851 | 0 |  |  |  |  |  | $args{data}= shift; | 
| 852 |  |  |  |  |  |  | } | 
| 853 |  |  |  |  |  |  |  | 
| 854 | 0 |  |  |  |  |  | return RapidApp::Error->new(\%args); | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  |  | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  |  | 
| 859 |  |  |  |  |  |  | # Suger function sets up a Native Trait ArrayRef attribute with useful | 
| 860 |  |  |  |  |  |  | # default accessor methods | 
| 861 |  |  |  |  |  |  | #sub hasarray { | 
| 862 |  |  |  |  |  |  | #  my $name = shift; | 
| 863 |  |  |  |  |  |  | #  my %opt = @_; | 
| 864 |  |  |  |  |  |  | # | 
| 865 |  |  |  |  |  |  | #  my %defaults = ( | 
| 866 |  |  |  |  |  |  | #    is => 'ro', | 
| 867 |  |  |  |  |  |  | #    isa => 'ArrayRef', | 
| 868 |  |  |  |  |  |  | #    traits => [ 'Array' ], | 
| 869 |  |  |  |  |  |  | #    default => sub {[]}, | 
| 870 |  |  |  |  |  |  | #    handles => { | 
| 871 |  |  |  |  |  |  | #      'all_' . $name => 'uniq', | 
| 872 |  |  |  |  |  |  | #      'add_' . $name => 'push', | 
| 873 |  |  |  |  |  |  | #      'insert_' . $name => 'unshift', | 
| 874 |  |  |  |  |  |  | #      'has_no_' . $name => 'is_empty', | 
| 875 |  |  |  |  |  |  | #      'count_' . $name    => 'count' | 
| 876 |  |  |  |  |  |  | #    } | 
| 877 |  |  |  |  |  |  | #  ); | 
| 878 |  |  |  |  |  |  | # | 
| 879 |  |  |  |  |  |  | #  my $conf = merge(\%defaults,\%opt); | 
| 880 |  |  |  |  |  |  | #  return caller->can('has')->($name,%$conf); | 
| 881 |  |  |  |  |  |  | #} | 
| 882 |  |  |  |  |  |  |  | 
| 883 |  |  |  |  |  |  | # Suger function sets up a Native Trait HashRef attribute with useful | 
| 884 |  |  |  |  |  |  | # default accessor methods | 
| 885 |  |  |  |  |  |  | #sub hashash { | 
| 886 |  |  |  |  |  |  | #  my $name = shift; | 
| 887 |  |  |  |  |  |  | #  my %opt = @_; | 
| 888 |  |  |  |  |  |  | # | 
| 889 |  |  |  |  |  |  | #  my %defaults = ( | 
| 890 |  |  |  |  |  |  | #    is => 'ro', | 
| 891 |  |  |  |  |  |  | #    isa => 'HashRef', | 
| 892 |  |  |  |  |  |  | #    traits => [ 'Hash' ], | 
| 893 |  |  |  |  |  |  | #    default => sub {{}}, | 
| 894 |  |  |  |  |  |  | #    handles => { | 
| 895 |  |  |  |  |  |  | #      'apply_' . $name    => 'set', | 
| 896 |  |  |  |  |  |  | #      'get_' . $name      => 'get', | 
| 897 |  |  |  |  |  |  | #      'has_' . $name      => 'exists', | 
| 898 |  |  |  |  |  |  | #      'all_' . $name      => 'values', | 
| 899 |  |  |  |  |  |  | #      $name . '_names'    => 'keys', | 
| 900 |  |  |  |  |  |  | #    } | 
| 901 |  |  |  |  |  |  | #  ); | 
| 902 |  |  |  |  |  |  | # | 
| 903 |  |  |  |  |  |  | #  my $conf = merge(\%defaults,\%opt); | 
| 904 |  |  |  |  |  |  | #  return caller->can('has')->($name,%$conf); | 
| 905 |  |  |  |  |  |  | #} | 
| 906 |  |  |  |  |  |  |  | 
| 907 |  |  |  |  |  |  |  | 
| 908 |  |  |  |  |  |  | sub infostatus { | 
| 909 | 0 |  |  | 0 | 0 |  | my %opt = @_; | 
| 910 | 0 | 0 |  |  |  |  | %opt = ( msg => $_[0] ) if (@_ == 1); | 
| 911 | 0 |  |  |  |  |  | return RapidApp::Responder::InfoStatus->new(%opt); | 
| 912 |  |  |  |  |  |  | } | 
| 913 |  |  |  |  |  |  |  | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | # ----- | 
| 916 |  |  |  |  |  |  | # New sugar automates usage of CustomPrompt for the purposes of a simple | 
| 917 |  |  |  |  |  |  | # message with Ok/Cancel buttons. Returns the string name of the button | 
| 918 |  |  |  |  |  |  | # after the prompt round-trip. Example usage: | 
| 919 |  |  |  |  |  |  | # | 
| 920 |  |  |  |  |  |  | # if(throw_prompt_ok("really blah?") eq 'Ok') { | 
| 921 |  |  |  |  |  |  | #   # do blah ... | 
| 922 |  |  |  |  |  |  | # } | 
| 923 |  |  |  |  |  |  | # | 
| 924 |  |  |  |  |  |  | sub throw_prompt_ok { | 
| 925 | 0 |  |  | 0 | 0 |  | my $msg; | 
| 926 | 0 | 0 | 0 |  |  |  | $msg = shift if (scalar(@_) % 2 && ! (ref $_[0])); # argument list is odd, and first arg not a ref | 
| 927 |  |  |  |  |  |  |  | 
| 928 | 0 | 0 | 0 |  |  |  | my %opt = (ref($_[0]) && ref($_[0]) eq 'HASH') ? %{$_[0]} : @_; | 
|  | 0 |  |  |  |  |  |  | 
| 929 |  |  |  |  |  |  |  | 
| 930 | 0 |  | 0 |  |  |  | $msg ||= $opt{msg}; | 
| 931 | 0 | 0 |  |  |  |  | $msg or die 'throw_prompt_ok(): must supply a "msg" as either first arg or named in hash key'; | 
| 932 |  |  |  |  |  |  |  | 
| 933 | 0 | 0 |  |  |  |  | my $c = RapidApp->active_request_context or die join(' ', | 
| 934 |  |  |  |  |  |  | 'throw_prompt_ok(): this sugar function can only be called from', | 
| 935 |  |  |  |  |  |  | 'within the context of an active request' | 
| 936 |  |  |  |  |  |  | ); | 
| 937 |  |  |  |  |  |  |  | 
| 938 | 0 | 0 |  |  |  |  | $c->is_ra_ajax_req or die die join(' ', | 
| 939 |  |  |  |  |  |  | 'throw_prompt_ok(): this sugar function can only be called from', | 
| 940 |  |  |  |  |  |  | 'within the context of a RapidApp-generated Ajax request' | 
| 941 |  |  |  |  |  |  | ); | 
| 942 |  |  |  |  |  |  |  | 
| 943 | 0 |  |  |  |  |  | my %cust_prompt = ( | 
| 944 |  |  |  |  |  |  | title	=> 'Confirm', | 
| 945 |  |  |  |  |  |  | items	=> { | 
| 946 |  |  |  |  |  |  | html => $msg | 
| 947 |  |  |  |  |  |  | }, | 
| 948 |  |  |  |  |  |  | formpanel_cnf => { | 
| 949 |  |  |  |  |  |  | defaults => {} | 
| 950 |  |  |  |  |  |  | }, | 
| 951 |  |  |  |  |  |  | validate => \1, | 
| 952 |  |  |  |  |  |  | noCancel => \1, | 
| 953 |  |  |  |  |  |  | buttons	=> [ 'Ok', 'Cancel' ], | 
| 954 |  |  |  |  |  |  | EnterButton => 'Ok', | 
| 955 |  |  |  |  |  |  | EscButton => 'Cancel', | 
| 956 |  |  |  |  |  |  | height	=> 175, | 
| 957 |  |  |  |  |  |  | width	=> 350, | 
| 958 |  |  |  |  |  |  | %opt | 
| 959 |  |  |  |  |  |  | ); | 
| 960 |  |  |  |  |  |  |  | 
| 961 | 0 | 0 |  |  |  |  | if (my $button = $c->req->header('X-RapidApp-CustomPrompt-Button')){ | 
| 962 |  |  |  |  |  |  | # $button should contain 'Ok' or 'Cancel' (or whatever values were set in 'buttons') | 
| 963 | 0 |  |  |  |  |  | return $button; | 
| 964 |  |  |  |  |  |  | } | 
| 965 |  |  |  |  |  |  |  | 
| 966 | 0 |  |  |  |  |  | die RapidApp::Responder::CustomPrompt->new(\%cust_prompt); | 
| 967 |  |  |  |  |  |  | } | 
| 968 |  |  |  |  |  |  | # ----- | 
| 969 |  |  |  |  |  |  |  | 
| 970 |  |  |  |  |  |  |  | 
| 971 |  |  |  |  |  |  |  | 
| 972 |  |  |  |  |  |  |  | 
| 973 |  |  |  |  |  |  | ########################################################################################## | 
| 974 |  |  |  |  |  |  | ########################################################################################## | 
| 975 |  |  |  |  |  |  | # | 
| 976 |  |  |  |  |  |  | # Automatically export all functions defined above: | 
| 977 |  |  |  |  |  |  |  | 
| 978 | 6 |  |  | 6 |  | 60 | use Class::MOP::Class; | 
|  | 6 |  |  |  |  | 19 |  | 
|  | 6 |  |  |  |  | 1788 |  | 
| 979 |  |  |  |  |  |  |  | 
| 980 |  |  |  |  |  |  | my @pkg_methods = grep { ! ($_ =~ /^_/) } ( # Do not export funcs that start with '_' | 
| 981 |  |  |  |  |  |  | Class::MOP::Class | 
| 982 |  |  |  |  |  |  | ->initialize(__PACKAGE__) | 
| 983 |  |  |  |  |  |  | ->get_method_list | 
| 984 |  |  |  |  |  |  | ); | 
| 985 |  |  |  |  |  |  |  | 
| 986 |  |  |  |  |  |  | push @EXPORT_OK, @pkg_methods; | 
| 987 |  |  |  |  |  |  |  | 
| 988 |  |  |  |  |  |  | # | 
| 989 |  |  |  |  |  |  | ########################################################################################## | 
| 990 |  |  |  |  |  |  | ########################################################################################## | 
| 991 |  |  |  |  |  |  |  | 
| 992 |  |  |  |  |  |  | # The same as Catalyst::Utils::home but just a little bit more clever: | 
| 993 |  |  |  |  |  |  | sub find_app_home { | 
| 994 | 0 | 0 | 0 | 0 | 0 |  | $_[0] && $_[0] eq __PACKAGE__ and shift; | 
| 995 |  |  |  |  |  |  |  | 
| 996 | 0 |  |  |  |  |  | require Catalyst::Utils; | 
| 997 | 0 |  |  |  |  |  | require Module::Locate; | 
| 998 |  |  |  |  |  |  |  | 
| 999 | 0 | 0 |  |  |  |  | my $class = shift or die "find_app_home(): expected app class name argument"; | 
| 1000 |  |  |  |  |  |  |  | 
| 1001 | 0 |  |  |  |  |  | my $path = Catalyst::Utils::home($class); | 
| 1002 |  |  |  |  |  |  |  | 
| 1003 | 0 | 0 |  |  |  |  | unless($path) { | 
| 1004 |  |  |  |  |  |  | # make an $INC{ $key } style string from the class name | 
| 1005 | 0 |  |  |  |  |  | (my $file = "$class.pm") =~ s{::}{/}g; | 
| 1006 | 0 | 0 |  |  |  |  | unless ($INC{$file}) { | 
| 1007 | 0 | 0 |  |  |  |  | if(my $pm_path = Module::Locate::locate($class)) { | 
| 1008 | 0 |  |  |  |  |  | local $INC{$file} = $pm_path; | 
| 1009 | 0 |  |  |  |  |  | $path = Catalyst::Utils::home($class); | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  | } | 
| 1012 |  |  |  |  |  |  | } | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 | 0 |  |  |  |  |  | return $path; | 
| 1015 |  |  |  |  |  |  | } | 
| 1016 |  |  |  |  |  |  |  | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | 1; |