2013-08-23 15 views
8

Powielamy powoli naszą dużą aplikację Perla w kierunku interfejsów obiektowych, szczególnie w przypadku modeli danych. Denerwujące jest to, że ślady stosu stają się mniej przydatne. Aby dać sfabrykowany przykład: Przed.Jak mogę dopasować renderowanie obiektów w długim czasie?

sub send_message { 
    my ($user_id, $message) = @_; 
    ... 
    Carp::confess('test'); 
} 

# output: 
test at example.pm line 23 
    foo('42', 'Hello World') called at example.pl line 5 

Po.

sub send_message { 
    my ($user, $message) = @_; 
    ... 
    Carp::confess('test'); 
} 

# output: 
test at example.pm line 23 
    foo('MyApp::Model::User=HASH(0x2c94f68)', 'Hello World') called at example.pl line 5 

Więc teraz nie mogę zobaczyć, które użytkownik został przekazany do foo(), tylko widzę nazwę klasy (co już jest udokumentowane), a niektóre adres pamięci obiektu.

Próbowałem zainstalować operator stringification od klasy modelu za pomocą overload.pm:

use overload ('""' => \&stringify); 

sub stringify { 
    my ($self) = @_; 
    return sprintf '%s[id=%d]', ref($self), $self->id; 
} 

Ale to nie ma wpływu na longmess. Chciałbym coś takiego:

test at example.pm line 23 
    foo('MyApp::Model::User[id=42]', 'Hello World') called at example.pl line 5 

Oznacza to, że pierwszy parametr do foo() powinny być wyświetlane przy użyciu stringify() metody obiektu. Jak mogę to osiągnąć?

+1

Wygląda na to, że chcesz mieć własną funkcję '' die() '] (http://perldoc.perl.org/functions/die.html), w której możesz zaimplementować swój własny ślad, który wywołuje' stringify() 'na twoim obiekty. Wierzę, że możesz zaimplementować własną funkcję 'die()' używając '$ SIG {__ DIE __}' hook. –

+1

@PP. '$ SIG {__ DIE __}' jest prawdopodobnie uszkodzony. Proszę go nie używać. Ten program obsługi ma być wywołany jako ostatnia rzecz przed wyjściem programu po "umieraniu", a nie jako haczyk do modyfikowania formatowania komunikatów o błędach. Można zastąpić 'CORE :: GLOBAL :: die' lub' use subs 'die'', aby zaimplementować niestandardową 'die'. Dotyczy to także 'Carp :: confess', a nie' die'. – amon

Odpowiedz

11

Problem jest w tej części Carp.pm:

sub format_arg { 
    my $arg = shift; 
    if (ref($arg)) { 
     $arg = defined($overload::VERSION) ? overload::StrVal($arg) : "$arg"; 
    } 
    ... 
} 

Oznacza to, że gdy argument może być przeciążony obiekt, a następnie każdy przeciążanie stringification jest obchodzone z StrVal helper, który zmusza domyślny stringification.

Niestety, nie ma na to prostego sposobu. Wszystko, co możemy zrobić, to łatać małpy sub, np. Carp::format_arg.

BEGIN { 
    use overload(); 
    use Carp(); 
    no warnings 'redefine'; 
    my $orig = \&Carp::format_arg; 

    *Carp::format_arg = sub { 
    my ($arg) = @_; 
    if (ref $arg and my $stringify = overload::Method($arg, '""')) { 
     $_[0] = $stringify->($arg); 
    } 
    goto &$orig; 
    }; 
} 

Jak to jest, to jest nieeleganckie i powinny być wprowadzane do Pragma:

pliku Carp/string_overloading.pm:

package Carp::string_overloading; 

use strict; use warnings; 

use overload(); 
use Carp(); 

# remember the original format_arg method 
my $orig = \&Carp::format_arg; 
# This package is internal to Perl's warning system. 
$Carp::CarpInternal{ __PACKAGE__() }++; 

{ 
    no warnings 'redefine'; 
    *Carp::format_arg = sub { 
     my ($arg) = @_; 
     if ( ref($arg) 
      and in_effect(1 + Carp::long_error_loc) 
      and my $stringify = overload::Method($arg, '""') 
     ) { 
      $_[0] = $stringify->($arg); 
     } 
     goto &$orig; 
    }; 
} 

sub import { $^H{__PACKAGE__ . "/in_effect"} = 1 } 

sub unimport { $^H{__PACKAGE__ . "/in_effect"} = 0 } 

sub in_effect { 
    my $level = shift // 1; 
    return (caller $level)[10]{__PACKAGE__ . "/in_effect"}; 
} 

1; 

Następnie kod

use strict; use warnings; 

package Foo { 
    use Carp(); 

    use overload '""' => sub { 
     my $self = shift; 
     return sprintf '%s[%s]', ref $self, join ", ", @$self; 
    }; 

    use Carp::string_overloading; 
    sub foo { Carp::confess "as requested" } 

    no Carp::string_overloading; 
    sub bar { Carp::confess "as requested" } 
} 

my $foo = bless [1..3] => 'Foo'; 

eval { $foo->foo("foo") }; 
print [email protected]; 
eval { $foo->bar("bar") }; 
print [email protected]; 

wyjścia:

as requested at test.pl line 12. 
     Foo::foo('Foo[1, 2, 3]', 'foo') called at test.pl line 20 
     eval {...} called at test.pl line 20 
as requested at test.pl line 15. 
     Foo::bar('Foo=ARRAY(0x85468ec)', 'bar') called at test.pl line 22 
     eval {...} called at test.pl line 22 
+1

Co za ... niesamowite! –

+1

Poprzedni przykład był brzydki i tandetny. Zaktualizowałem poprawki błędów (np. Usunięto wcześniej ustalony poziom dzwoniącego) – amon

Powiązane problemy