From: Matt S Trout Date: Sat, 6 Feb 2010 19:39:46 +0000 (+0000) Subject: add peek method to streams, implement 'inside' for collect X-Git-Tag: release_0.009004~88 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FHTML-Zoom.git;a=commitdiff_plain;h=8f9628843b59b74bb8f8936460fb8676c410c35f add peek method to streams, implement 'inside' for collect --- diff --git a/lib/HTML/Zoom/CodeStream.pm b/lib/HTML/Zoom/CodeStream.pm index 7c8fefb..144effa 100644 --- a/lib/HTML/Zoom/CodeStream.pm +++ b/lib/HTML/Zoom/CodeStream.pm @@ -16,8 +16,27 @@ sub new { bless({ _code => $args->{code} }, $class); } +sub peek { + my ($self) = @_; + if (exists $self->{_peeked}) { + return ($self->{_peeked}); + } + if (my ($peeked) = $self->next) { + return ($self->{_peeked} = $peeked); + } + return; +} + sub next { - $_[0]->{_code}->() + my ($self) = @_; + + # peeked entry so return that + + if (exists $self->{_peeked}) { + return (delete $self->{_peeked}); + } + + $self->{_code}->(); } 1; diff --git a/lib/HTML/Zoom/FilterBuilder.pm b/lib/HTML/Zoom/FilterBuilder.pm index 7f9b7b6..e06d570 100644 --- a/lib/HTML/Zoom/FilterBuilder.pm +++ b/lib/HTML/Zoom/FilterBuilder.pm @@ -37,7 +37,7 @@ sub set_attribute { my ($self, $args) = @_; my ($name, $value) = @{$args}{qw(name value)}; sub { - my $a = (my $evt = shift)->{attrs}; + my $a = (my $evt = $_[0])->{attrs}; my $e = exists $a->{$name}; +{ %$evt, raw => undef, raw_attrs => undef, attrs => { %$a, $name => $value }, @@ -52,7 +52,7 @@ sub add_attribute { my ($self, $args) = @_; my ($name, $value) = @{$args}{qw(name value)}; sub { - my $a = (my $evt = shift)->{attrs}; + my $a = (my $evt = $_[0])->{attrs}; my $e = exists $a->{$name}; +{ %$evt, raw => undef, raw_attrs => undef, attrs => { @@ -70,7 +70,7 @@ sub remove_attribute { my ($self, $args) = @_; my $name = $args->{name}; sub { - my $a = (my $evt = shift)->{attrs}; + my $a = (my $evt = $_[0])->{attrs}; return $evt unless exists $a->{$name}; $a = { %$a }; delete $a->{$name}; +{ %$evt, raw => undef, raw_attrs => undef, @@ -82,25 +82,25 @@ sub remove_attribute { sub add_before { my ($self, $events) = @_; - sub { return $self->_stream_from_array(@$events, shift) }; + sub { return $self->_stream_from_array(@$events, $_[0]) }; } sub add_after { my ($self, $events) = @_; sub { - my ($evt, $stream) = @_; + my ($evt) = @_; my $emit = $self->_stream_from_array(@$events); my $coll = $self->collect({ passthrough => 1 })->(@_); return ref($coll) eq 'HASH' # single event, no collect ? [ $coll, $emit ] : [ $coll->[0], $self->_stream_concat($coll->[1], $emit) ]; }; -} +} sub prepend_inside { my ($self, $events) = @_; sub { - my $evt = shift; + my ($evt) = @_; if ($evt->{is_in_place_close}) { $evt = { %$evt }; delete @{$evt}{qw(raw is_in_place_close)}; return [ $evt, $self->_stream_from_array( @@ -111,6 +111,22 @@ sub prepend_inside { }; } +sub append_inside { + my ($self, $events) = @_; + sub { + my ($evt) = @_; + if ($evt->{is_in_place_close}) { + $evt = { %$evt }; delete @{$evt}{qw(raw is_in_place_close)}; + return [ $evt, $self->_stream_from_array( + @$events, { type => 'CLOSE', name => $evt->{name} } + ) ]; + } + my $coll = $self->collect({ passthrough => 1, inside => 1 })->(@_); + my $emit = $self->_stream_from_array(@$events); + return [ $coll->[0], $self->_stream_concat($coll->[1], $emit) ]; + }; +} + sub replace { my ($self, $events) = @_; sub { @@ -123,32 +139,36 @@ sub replace { sub collect { my ($self, $attrs) = @_; - my ($into, $passthrough) = @{$attrs}{qw(into passthrough)}; + my ($into, $passthrough, $inside) = @{$attrs}{qw(into passthrough inside)}; sub { my ($evt, $stream) = @_; - push(@$into, $evt) if $into; + push(@$into, $evt) if $into && !$inside; if ($evt->{is_in_place_close}) { - return $evt if $passthrough; + return $evt if $passthrough || $inside; return; } my $name = $evt->{name}; my $depth = 1; + my $_next = $inside ? 'peek' : 'next'; my $collector = $self->_stream_from_code(sub { return unless $stream; - while (my ($evt) = $stream->next) { + while (my ($evt) = $stream->$_next) { $depth++ if ($evt->{type} eq 'OPEN'); $depth-- if ($evt->{type} eq 'CLOSE'); - push(@$into, $evt) if $into; unless ($depth) { undef $stream; + return if $inside; + push(@$into, $evt) if $into; return $evt if $passthrough; return; } + push(@$into, $evt) if $into; + $stream->next if $inside; return $evt if $passthrough; } die "Never saw closing before end of source"; }); - return $passthrough ? [ $evt, $collector ] : $collector; + return ($passthrough||$inside) ? [ $evt, $collector ] : $collector; }; } diff --git a/lib/HTML/Zoom/FilterStream.pm b/lib/HTML/Zoom/FilterStream.pm index c698159..89605dd 100644 --- a/lib/HTML/Zoom/FilterStream.pm +++ b/lib/HTML/Zoom/FilterStream.pm @@ -15,9 +15,26 @@ sub new { ); } +sub peek { + my ($self) = @_; + if (exists $self->{_peeked}) { + return ($self->{_peeked}); + } + if (my ($peeked) = $self->next) { + return ($self->{_peeked} = $peeked); + } + return; +} + sub next { my ($self) = @_; + # peeked entry so return that + + if (exists $self->{_peeked}) { + return (delete $self->{_peeked}); + } + # if our main stream is already gone then we can short-circuit # straight out - there's no way for an alternate stream to be there diff --git a/t/actions.t b/t/actions.t index 8e315d5..625c48e 100644 --- a/t/actions.t +++ b/t/actions.t @@ -46,7 +46,55 @@ sub run_for (&;$) { ) } -(my $expect = $tmpl) =~ s/(?=set_attribute({ name => 'class', value => 'foo' }) }, + $expect, + 'set attribute on existing attribute' +); + +($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; + +is( + run_for { $_->set_attribute({ name => 'foo', value => 'bar' }) }, + $expect, + 'set attribute on non existing attribute' +); + +($expect = $tmpl) =~ s/class="main"/class="main foo"/; + +is( + run_for { $_->add_attribute({ name => 'class', value => 'foo' }) }, + $expect, + 'add attribute on existing attribute' +); + +($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; + +is( + run_for { $_->add_attribute({ name => 'foo', value => 'bar' }) }, + $expect, + 'add attribute on non existing attribute' +); + +($expect = $tmpl) =~ s/ class="main"//; + +is( + run_for { $_->remove_attribute({ name => 'class' }) }, + $expect, + 'remove attribute on existing attribute' +); + +is( + run_for { $_->remove_attribute({ name => 'foo' }) }, + $tmpl, + 'remove attribute on non existing attribute' +); + +($expect = $tmpl) =~ s/(?= 'TEXT', raw => 'O HAI' } ]; @@ -89,7 +137,7 @@ is( 'replace ok' ); -my @ev; +@ev = (); is( run_for { $_->collect({ into => \@ev }) }, @@ -110,50 +158,25 @@ is( 'collect collected right events' ); -($expect = $tmpl) =~ s/class="main"/class="foo"/; - -is( - run_for { $_->set_attribute({ name => 'class', value => 'foo' }) }, - $expect, - 'set attribute on existing attribute' -); - -($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; - -is( - run_for { $_->set_attribute({ name => 'foo', value => 'bar' }) }, - $expect, - 'set attribute on non existing attribute' -); - -($expect = $tmpl) =~ s/class="main"/class="main foo"/; - -is( - run_for { $_->add_attribute({ name => 'class', value => 'foo' }) }, - $expect, - 'add attribute on existing attribute' -); - -($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; - -is( - run_for { $_->add_attribute({ name => 'foo', value => 'bar' }) }, - $expect, - 'add attribute on non existing attribute' -); - -($expect = $tmpl) =~ s/ class="main"//; +@ev = (); is( - run_for { $_->remove_attribute({ name => 'class' }) }, - $expect, - 'remove attribute on existing attribute' + run_for { $_->collect({ into => \@ev, inside => 1 }) }, + ' +
+ +', + 'collect w/inside removes correctly' ); is( - run_for { $_->remove_attribute({ name => 'foo' }) }, - $tmpl, - 'remove attribute on non existing attribute' + HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev), + ' + Bob + Builder +
+ ', + 'collect w/inside collects correctly' ); done_testing;