X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FHTML%2FString%2FValue.pm;h=d6bac14cc9fdde7bc7a371c27eb2c42e6951de23;hb=4f4204a3cb10eac31bb4387275f10753d25bf07e;hp=cb73ad2770282dd2ee6249581bc5b63dd039c564;hpb=b8aaa17d237994dae86b61a6aa96040fff75f425;p=scpubgit%2FHTML-String.git diff --git a/lib/HTML/String/Value.pm b/lib/HTML/String/Value.pm index cb73ad2..d6bac14 100644 --- a/lib/HTML/String/Value.pm +++ b/lib/HTML/String/Value.pm @@ -3,6 +3,7 @@ package HTML::String::Value; use strictures 1; use UNIVERSAL::ref; use Safe::Isa; +use Scalar::Util qw(blessed); use Data::Munge; use overload @@ -14,7 +15,10 @@ use overload ; sub new { - if (ref($_[0])) { my $c = shift; return $c->_hsv_unescaped_string->new(@_) } + if (blessed($_[0])) { + my $c = shift; + return $c->_hsv_unescaped_string->new(@_); + } my ($class, @raw_parts) = @_; my $opts = (ref($raw_parts[-1]) eq 'HASH') ? pop(@raw_parts) : {}; @@ -56,6 +60,7 @@ sub _hsv_escaped_string { s//>/g; s/"/"/g; + s/'/'/g; } $_->[0] : $_->[0] ), @{$self->{parts}}; @@ -70,7 +75,7 @@ sub _hsv_unescaped_string { sub _hsv_dot { my ($self, $str, $prefix) = @_; - return $self unless $str; + return $self unless defined $str && length $str; my @parts = @{$self->{parts}}; @@ -86,14 +91,212 @@ sub _hsv_dot { push @parts, @new_parts; } - return ref($self)->new(@parts, { ignore => $self->{ignore} }); + return bless({ %$self, parts => \@parts }, blessed($self)); } sub _hsv_is_true { my ($self) = @_; - return 1 if grep length($_), map $_->[0], @{$self->{parts}}; + return 1 if grep $_, map $_->[0], @{$self->{parts}}; +} + +# we need to local $@ here because some modules (cough, TT, cough) +# will do a 'die $@ if $@' without realising that it wasn't their eval +# that set it + +sub isa { + my $self = shift; + return ( + do { + local $@; + eval { blessed($self) and $self->_hsv_unescaped_string->isa(@_) } + } + or $self->SUPER::isa(@_) + ); +} + +sub can { + my $self = shift; + return ( + do { + local $@; + eval { blessed($self) and $self->_hsv_unescaped_string->can(@_) } + } + or $self->SUPER::can(@_) + ); } sub ref { '' } +sub DESTROY { } + 1; + +__END__ + +=head1 NAME + +HTML::String::Value - A scalar hiding as a string on behalf of L + +=head1 SYNOPSIS + +Usually, you'd create this with L's L export +but: + + my $html = HTML::String::Value->new($do_not_escape_this); + + my $html = HTML::String::Value->new([ $do_not_escape_this, 0 ]); + + my $html = HTML::String::Value->new([ $do_escape_this, 1 ]); + + my $html = HTML::String::Value->new($already_an_html_string_value); + + my $html = HTML::String::Value->new(@an_array_of_any_of_the_above); + + my $html = HTML::String::Value->new( + @parts, { ignore => { package_name => 1 } } + ); + +=head1 METHODS + +=head2 new + + my $html = HTML::String::Value->new(@parts, \%options?); + +Each entry in @parts consists of one of: + + 'some text that will not be escaped' + + [ 'some text that will not be escaped', 0 ] + + [ 'text that you DO want to be escaped', 1 ] + + $existing_html_string_value + +Currently, the %options hashref contains only: + + ( + ignore => { 'Package::One' => 1, 'Package::Two' => 1, ... } + ) + +which tells this value object to ignore whether escaping has been requested +for any particular chunk and instead to provide the unescaped version. + +When called on an existing object, does the equivalent of + + $self->_hsv_unescaped_string->new(@args); + +to fit in with the "pretending to be a class name" behaviour provided by +L. + +=head2 _hsv_escaped_string + + $html->_hsv_escaped_string + +Returns a concatenation of all parts of this value with those marked for +escaping escaped, unless the calling package has been specified in the +C option to L. + +If the calling package has been marked as ignoring escaping, returns the +result of L. + +You probably shouldn't be calling this directly. + +=head2 _hsv_unescaped_string + + $html->_hsv_unescaped_string + +Returns a concatenation of all parts of this value with no escaping performed. + +You probably shouldn't be calling this directly. + +=head2 _hsv_dot + + $html->_hsv_dot($other_string, $reversed) + +Returns a new value object consisting of the two values' parts concatenated +together (in reverse if C<$reversed> is true). + +Unlike L, this method defaults to escaping a bare string provided. + +You probably shouldn't be calling this directly. + +=head2 _hsv_is_true + + $html->_hsv_is_true + +Returns true if any of this value's parts are true. + +You probably shouldn't be calling this directly. + +=head2 AUTOLOAD + + $html->method_name(@args) + +This calls the equivalent of + + $html->_hsv_unescaped_string->method_name(@args) + +to allow for class method calls even when the class name has ended up being +turned into a value object. + +=head2 isa + + $html->isa($name) + +This method returns true if either the value or the unescaped string are +isa the supplied C<$name> in order to allow for class method calls even when +the class name has ended up being turned into a value object. + +=head2 can + + $html->can($name) + +This method returns a coderef if either the value or the unescaped string +provides this method; methods on the unescaped string are preferred to allow +for class method calls even when the class name has ended up being +turned into a value object. + +=head2 ref + + $html->ref + +This method always returns C<''>. Since we register ourselves with +L, this means that + + ref($html); + +will also return C<''>, which means that modules loaded after this one will +see a value object as being a plain scalar unless they're explicitly checking +the defined-ness of the return value of C, which probably means that they +wanted to spot people cheating like we're trying to. + +If you have trouble with things trying to treat a value object as something +other than a string, try loading L earlier. + +=head2 DESTROY + +Overridden to do nothing so that L doesn't trap it. + +=head1 OPERATOR OVERLOADS + +=head2 stringification + +Stringification is overloaded to call L + +=head2 concatenation + +Concatentation is overloaded to call L + +=head2 boolification + +Boolification is overloaded to call L + +=head1 AUTHORS + +See L for authors. + +=head1 COPYRIGHT AND LICENSE + +See L for the copyright and license. + +=cut